11

【设计模式】第六篇:来康康适配器模式

 3 years ago
source link: https://segmentfault.com/a/1190000038175009
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

j2eyI3u.png!mobile

一 引言

设计模式就是一种思想,一种设计方式,他可以帮助我们对于一些问题(不一定是技术,如生活中的问题)的处理提供一些思路,以及问题解决方案的建模与描述

例如我们今天要讲的适配器模式就是从解决生活问题,过渡到了技术层面的问题

现实生活中,常常会出现两者不能兼容的一种情况,例如出国了,我们从国内带的充电插头与外国的插孔就不匹配,再例如我去香港旅游,但是又不会讲粤语,在一些小一些的商店买东西,就需要一个会讲粤语的朋友帮我在中间翻译

换到技术层面中,我们开发某些业务的部分组件已经在库中存在了,但是由于过去开发组件时与当前接口规范不兼容,使用适配器模式,也就是找一个中间量,帮助我们适配两者,对接起来,这样就不用再开发一个新的组件了

二 代码演示

例子背景是这样的,我们现在有一台苹果手机(Lightning 接口),但是我们现在只有一根 Type-C 的充电线,我们需要想办法,增加一个中间的转接器,从而能达到给手机充电的效果

(一) 类适配器(继承,不常用)

(1) 演示

虽然这一种不常用,但是第二种就是在第一种基础上改进的,内容不是很多,可以耐心看完喔

首先,定义一个苹果手机类,其中有一个充电的方法(Converter 在后面有说,接着往下看)

/**
 * 客户端类:苹果手机想充电,但是充电线的头是 Type-C 的
 */
public class Phone {
    // 充电方法
    public void charging(Converter converter){
        converter.TypeCToLightning();
    }
}

接着定义的就是 Type-C 这条充电线,因为它是没办法插入苹果手机的 Lightning 接口 的,所以它就是被适配的类

/**
 * 被适配的类:Type-C充电线
 */
public class TypeCLine {

    public void charging(){
        System.out.println("开始充电了");
    }
}

两个不能直接联系的内容,我们已经定义好了,即手机和 Type-C 充电线,现在就差一个中间的适配器了,现在可以定义一个转换的接口,其中定义一个Type-C 转为 Lightning 的方法

/**
 * 充电器转换接口
 */
public interface Converter {
    // Type-C 转为 苹果 Lightning 接口
    void TypeCToLightning();
}

接下来就是一个具体的实现类了,实现抽象接口没什么好说的,还有一步是关键——继承充电线这个需要被适配的类,至于它的利弊我们下面再说

/**
 * Type-C 充电线转化器
 */
public class TypeCLineAdapter extends TypeCLine implements Converter {
    @Override
    public void TypeCToLightning() {
        // 转接成功,可以使用经过转接后的 type-C 的线充苹果手机了
        super.charging(); 
    }
}

测试一下

public class Test {
    public static void main(String[] args) {
        Phone phone = new Phone(); // 手机
        TypeCLine typeCLine = new TypeCLine(); // Type-C充电线
        Converter typeCLineAdapter = new TypeCLineAdapter(); // Type-C充电线转接器
        // 手机直接引入转接器从而充电
        phone.charging(typeCLineAdapter);
    }
}

结果:开始充电了

(2) 利弊

测试代码中,大家应该也能看出来了 typeCLine 好像并没有被用到,确实如此,因为我们在适配器类,即 TypeCLineAdapter 类中,是通过直接继承 TypeCLine 这个 Type-C 充电线类的,也就是说,它的理念是通过多重继承来实现两个接口之间进行匹配,但是像 C#,VB.NET,Java等语言都是不支持多继承的,而且其使用了继承,继承在控制不恰当的情况下总会给我们带来一些麻烦,所以在这里也并不是很合适,所以就有了下面的方式

(二) 对象适配器(组合,常用)

(1) 演示

手机和 Type-C 充电线这是不变的,需要做的就是修改具体的适配器类 TypeCLineAdapter,我们这里创建一个新的 TypeCLineAdapter2

/**
 * Type-C 充电线转化器
 */
public class TypeCLineAdapter2 implements Converter {

    private TypeCLine typeCLine;

    public TypeCLineAdapter2(TypeCLine typeCLine) {
        this.typeCLine = typeCLine;
    }

    @Override
    public void TypeCToLightning() {
        // 转接成功,可以使用经过转接后的 type-C 的线充苹果手机了
        typeCLine.charging(); 
    }
}

测试一下:

public class Test {
    public static void main(String[] args) {
        Phone phone = new Phone(); // 手机
        TypeCLine typeCLine = new TypeCLine(); // Type-C充电线
        Converter typeCLineAdapter2 = new TypeCLineAdapter2(typeCLine); // Type-C充电线转接器
        // 手机直接引入转接器从而充电
        phone.charging(typeCLineAdapter2);
    }
}

(2) 利弊

在 TypeCLineAdapter2 的步骤中,首先我们取消了适配器继承 TypeCLine 类,而是选择通过在适配器TypeCLineAdapter2 这个类 中增加一个 TypeCLine 的私有域,然后再在方法中调用

首先,我们摆脱了单继承带来的烦恼,那么这样做还有什么好处吗

在 《Effecttive Java》一书中,第 4 章 第 18 条:复合优先于继承中有提到,有一个比较好的说法,我简单摘了一下:

我们可以创建一个新的类(适配器 TypeCLineAdapter2 类),在新的类中添加一个私有域(TypeCLine 类),他引用现有类的一个实例,这种设计叫做复合

  • 因此现有的类变成了新类的一个组件,新类中的每个实例方法都可以调用被包含的现有的类中对应的方法,并返回他的结果,这就是转发,而新类中的方法被称为 转发方法

小结:这种方式下,测试的时候感觉也会更舒服,和我们想的一般,手机和 Type-C 的线,分别通过链接适配器,就达到了能适配充电的方式,同样又避免了使用继承,算是一种比较好的适配问题处理方式

三 适配器模式理论

(一) 定义和分类

适配器模式(Adapter)定义:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作

分类:

  • 类适配器模式

    • 主要使用继承实现,耦合度高,且在单继承的语言中使用会受限,还需要防止继承带来的一些问题
  • 对象适配器模式

    • 使用了组合(或叫复合)的方式降低了耦合,推荐使用

(二) 结构

① 类适配器模式

6jaE7vU.png!mobile

② 对象适配器模式

qyyYjaA.png!mobile

还是依旧分析一下其中的角色:

适配者(Adaptee)类:它是需要被适配的类,例如上面提到的 Type-C 线,适配后才能插入到苹果手机中充电

目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口,对应代码中的 Converter 接口,他是客户期待的适配转换接口

适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK