设计模式系列之「适配器模式」

vczh的日常 2017-11-30

设计模式系列之「适配器模式」

小C:小Y,家里的插孔没有两孔的怎么办?
小Y:so easy,淘宝电源转换插头包邮只要九块九毛九,真的只要九块九毛九。
......
小C:iPhone x变成Lightning接头,传统的耳机会不会用不了?
小Y:又没有这么牛叉的机,你问这个干啥呢(小Y有点不好的预感啊)?买个转换线呗。
小C:哦,没事就问问。

后来呢,小Y几个月只能吃泡面了过日子了,呜呜呜......

其实大家也要学学小Y这种为博红颜一笑,勒紧裤头吃泡面的无谓精神(一定要看好自己的信用卡啊)。言归正传,现实生活中有各种各样的适配器,方便至极,那么,适配器这样的工作,在我们的程序里有吗?答案是必须的,适配器模式。

设计模式系列之「适配器模式」文章内容思维导图

一、基本概念

1.定义

将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。即定义一个包装类,用于包装不兼容接口的对象。

2.应用场景
  • 有动机修改一个已经上线的接口时并且不符合你的需求,适配器模式可能是最适合你的模式。
3.角色介绍
设计模式系列之「适配器模式」通用UML.png
  • Target目标角色

    该角色定义把其他类转换为何种接口,也就是我们的期望接口。

  • Adaptee源角色

    需要转换成目标角色的源角色,它是已经存在的、运行良好的类或对象,经过适配器角色的包装,它会成为一个崭新的角色。

  • Adapter适配器角色

    适配器模式的核心角色,其他两个角色都是已经存在的角色,而适配器角色是需要新建立的,它的职责非常简单:把源角色通过继承或者类关联的方法转换为目标角色。

二、代码的实现

好了,该是让这次的主角iPhone差的转换线出场了(无限心伤中.....)

设计模式系列之「适配器模式」

在这之前还需要知道适配器模式的形式分为:类的适配器模式对象的适配器模式

1.两种不同形式适配模式的UML对比

设计模式系列之「适配器模式」

两者的差别就是一个是通过继承,一个是通过组合,利用实例的方式去进行

2.两种不同形式适配模式的代码实现

(1)两者实现代码的相同部分

①iPhone X手机接口(Target接口)

public interface PhoneInterface {  
    /**  
     * Lightning耳机接口
     */  
    void lightningHeadSet();  
}

②Lightning接口耳机

public class LightningHeadSet implements PhoneInterface {  
    @Override  
    public void lightningHeadSet() {  
        System.out.print("使用Lightning接口的耳机连接。");  
    }  
}

③传统耳机接口(源类Adaptee)

public interface TraditionPhoneInterface {   
    /**  
     * 传统耳机接口  
     */  
    void traditionHeadSet();  
}

④传统接口耳机

public class TraditionHeadSet implements TraditionPhoneInterface{  

    @Override  
    public void traditionHeadSet() {  
        System.out.print("使用传统接口的耳机连接。");  
    }  
}

(2)类的适配器模式的实现

创建适配器类(继承传统耳机(源类)和实现Linghtning接口(Target))

public class HeadSetAdapter extends TraditionHeadSet implements PhoneInterface{  
    @Override  
    public void lightningHeadSet() {  
        super.traditionHeadSet();  
    }  
}

为什么会去继承TraditionHeadSet和实现PhoneInterface呢?是因为用户最终是需要用到Lightning接口去实现听歌,所以要调用lightningHeadSet(),但是适配器的作用就是让传统耳机实现Lightning的转化,最终用传统耳机都能够得到连接。

(3)对象的适配器模式的实现(推荐)

public class ObjectHeadSetAdapter implements PhoneInterface {  

    private TraditionPhoneInterface traditionHeadSet;  

    public ObjectHeadSetAdapter(TraditionPhoneInterface traditionHeadSet) {  
        this.traditionHeadSet=traditionHeadSet;  
    }  

    @Override  
    public void lightningHeadSet() {  
        traditionHeadSet.traditionHeadSet();  
    }  
}

为什么只实现PhoneInterface接口的理由同上。

(4)客户端输出

public class Client {
    public static void main(String[] args){
        //直接通过lightning接口连接
        LightningHeadSet lightningHeadSet=new LightningHeadSet();
        lightningHeadSet.lightningHeadSet();
        //通过类的适配器进行连接
        HeadSetAdapter headSetAdapter=new HeadSetAdapter();
        headSetAdapter.lightningHeadSet();
        //通过对象的适配器进行连接
        TraditionPhoneInterface traditionSet=new TraditionHeadSet();
        ObjectHeadSetAdapter objectHeadSetAdapter=new ObjectHeadSetAdapter(traditionSet);
        objectHeadSetAdapter.lightningHeadSet();
    }
}

输出的结果为:

①使用Lightning接口的耳机连接。  
②使用传统接口的耳机连接。  
③使用传统接口的耳机连接。

3.对象的适配器 vs 类的适配器

  • 类的适配器中Adapter与Adaptee是继承关系,因为java是不支持多继承的,因此不适合出现多个源类;对象的适配器模式不是使用继承关系连接到Adaptee类,而是使用委派关系连接到Adaptee类,这种关联的方法可以动态组合多个源类。
  • 类的适配器是继承被适配类,所以相对静态;而对象的适配器包含被适配类,所以相对灵活。
  • 类的适配器通过 Override 来扩展新需求;而对象的适配器因为包含关系所以不能扩展。

三、优缺点

1.类的适配器的优缺点

(1)优点

  • 使用方便,代码简化,不需要引入对象实例。

(2)缺点

  • 高耦合,灵活性低。使用对象继承的方式,是静态的定义方式。

2.对象的适配器的优缺点

(1)优点

  • 灵活性高、低耦合 ,采用 “对象组合”的方式,是动态组合方式。

(2)缺点

  • 使用复杂,需要引入对象实例。

3.适配器的优缺点

(1)优点

  • 提高了类的复用度。源角色在原有的系统中可以正常使用,在目标角色中又可以充当新的角色。
  • 增加了类的透明性,客户端可以调用同一接口,因而对客户端来说是透明的。
  • 灵活性好。增加删除不影响原有代码。
  • 符合开闭原则。

(2)缺点

  • 过多的使用适配器,会让系统非常零乱,不易整体进行把握。

四、总结

在选用适配器模式的时候根据需要来选用合适的实现方式,尽量使用对象的适配器模式,多用动态组合合、少用继承。

设计模式系列之「适配器模式」Android技术交流吧

相关推荐