设计模式之适配器模式

《Head First 设计模式》阅读笔记

Posted by wangtiegang on November 17, 2019

适配器模式

适配器模式将一个类的接口,转换成客户期望的另一个接口,适配器让原本接口不兼容的类可以合作无间。

适配器模式是一个很简单的设计模式,在日常生活中有很多相似的例子,比如去欧洲国家,他们的插座都是三孔的,电压也不是220V,如果我们国产电器要使用的话,就必须使用适配器来转换接口和电压,如果有其他国家不同的标准,再提供一个其他适配器就好了,这样就不必为了兼容不同电器而设计多种电路。它的核心思想就是利用已有的对象,通过封装调用,转换成客户端需要的,能直接兼容的对象。

如果通过代码来解释的话,那么就是下面的样子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/**
* 欧洲国家标准,无法直接使用
*/
public class EuropePower {

    // 三孔
    public String getThreeHoles(){
        return "ThreeHoles";
    }

    // 240V电压
    public int getVoltage(){
        return 240;
    }

}

/**
* 国内标准,两孔,220V
*/
public interface ChinaPower {

    public String getTwoHoles();

    public int getVoltage();

}

/**
* 创建一个适配器
*/
public class PowerAdapter implements ChinaPower {

    // 欧洲电力(被适配者)
    EuropePower europePower;

    public PowerAdapter(EuropePower europePower){
        this.europePower = europePower;
    }

    // 调用此方法将得到两孔插座
    public String getTwoHoles(){
        String three = europePower.getThreeHoles();
        // 将三孔转换成两孔
        return threeToTwo(three);
    }

    // 调用此方法将得到220V电压
    public int getVoltage(){
        int v240 = europePower.getVoltage();
        // 将240转成220
        return v240To220(v240);
    }

    private String threeToTwo(String three){
        // 具体实现...
        return "two";
    }

    private int v240To220(int v240){
        // 具体实现...
        return 220;
    }

}

// 测试
public class Test{
    public void static main(String[] args){
        // 将欧洲标准传给适配器
        PowerAdapter adapter = new PowerAdapter(new EuropePower());
        // 通过适配器获得国内标准的接口,两孔和220V电压
        adapter.getTwoHoles();
        adapter.getVoltage();  
    }
}

以上代码就是适配器模式,这个模式可以让客户端从实现中解藕,如果实现修改了,可以通过适配器将改变的地方封装起来,客户端就不必要跟着修改。适配器模式具有良好的设计原则:使用了组合,被适配者任何子类都可以搭配适配器使用。

上面的代码例子其实是对象适配器,还有一种是类适配器。对象适配器通过组合方式来拥有被适配的对象(被适配的对象可以是多个),然后进行转换。而类适配器通过继承来拥有被适配对象的实现(继承一个或多个),因为java是单继承,无法同时适配多个对象,所以这种模式适合多继承的语言,此处不做过多介绍。

外观模式

作者在适配器模式章节中顺便提到了外观模式,这个模式就更简单了。

外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观模式定义了一个高层接口,让子系统更容易使用。

这个模式就相当于快捷操作方式一样,只需要调用一下,程序就按顺序去做一系列的事情。比如原来你需要按顺序去调用1,2,3,4,5,6个方法才能得到一个结果,现在使用外观模式定义一个方法b,方法b中按顺序调用了1,2,3,4,5,6个方法,这样你直接调用b就行了,而不必要去调用6个方法。外观模式让客户端和一系列接口松耦合,让更加容易使用,同时如果需要,也可以直接调用子系统获得更多功能。

最少知识原则

最少知识原则:只和你的密友谈话

上面介绍的外观模式,其实蕴含着一种设计原则,那就是最少知识原则。就是说,当你设计一个系统时,不管是任何对象,你都要注意它所交互的类有哪些,并注意是如何交互的,不应该让太多的类耦合在一起,免得修改一部分,会影响到其他部分。

这个原则提供了一些指导方针,在一个对象中,我们应该只调用属于以下范围的方法:

  • 该对象本身
  • 被当作方法的参数而传递进来的对象
  • 此方法所创建或实例化的任何对象
  • 对象的任何组件,也就是通过组合方式拥有的属性对象
1
2
3
4
5
6
7
8
9
10
11
public float getTemp(){
    // 不推荐,thermometer对象是通过station得到的
    Thermometer thermometer = station.getThermometer();
    return thermometer.getTemperature();
}

public float getTemp(){
    // 推荐,通过组件station调用方法getTemperature,然后在station中去调用thermometer.getTemperature()
    // 这样就将thermometer对象松耦合了
    return station.getTemperature();
}

到这个章节,已经接触到了7个设计原则,分别是:

  • 封装变化
  • 多用组合,少用继承
  • 针对接口编程,不针对实现编程
  • 为交互对象之间的松耦合设计而努力
  • 类应该对扩展开放,对修改关闭
  • 依赖抽象,不要依赖具体类
  • 只和密友交谈

不管是设计模式还是设计原则,在带来良好设计的同时,也可能带来一些负面影响,比如增加了很多对象封装,提高了系统的复杂性,降低了系统的性能,所以在实际工作中得学会取舍,达到一个平衡。