适配器模式提供了将一种对象转换成另一种对象的能力,利用它可以实现两个不兼容接口的协调工作。外观模式是封装对象内的复杂逻辑,对外提供一个简化的接口。
1. 适配器模式
生活中最常见的适配器的使用场景就是各种转换线和转换插头,例如投影仪到电脑之间的转换线,港版iPhone和国内插座之间的转换插头等。下面就以iPhone和插座之间的转换插头为例来看看适配器模式的用法。
港版iPhone使用的英式插头,如左图所示,这种插头没法直接插在国内的标准插座上,要使用类似右图的转换器才行,那么现在需要做的就是找到一个转换器(适配器),将英式插头转换成符合国内标准的插头,也就是将EnglishPlug转换成ChinesePlug。(注:下面两个图片来源:https://zhidao.baidu.com/question/1365859698524477699.html)
先看一下插头的定义:
1 public interface EnglishPlug{ 2 void charge(); // 英式插头可以插到插座上充电 3 } 4 5 public interface ChinesePlug{ 6 void charge(); // 中式插头也可以插到插座上充电 7 }
接下来是英式iPhone插头和中式iPhone插头:
1 public class EnglishiPhonePlug implements EnglishPlug{ 2 @Override 3 public void charge(){ 4 System.out.println("英式插头在充电"); 5 } 6 } 7 8 public class ChineseiPhonePlug implements ChinesePlug{ 9 @Override 10 public void charge(){ 11 System.out.println("中式插头在充电"); 12 } 13 }
由于两个插头都已经做好了,回炉重造肯定是不可能了,只能增加一个转换器PlugConverter,将EnglishPlug伪装成ChinesePlug,好让英式插头能在中式插座上充电。要伪装成ChinesePlug,就需要让PlugConverter实现ChinesePlug接口,在转换器中组合EnglishPlug来完成转换工作,代码如下:
1 publich class IPhonePlugConverter implements ChinesePlug{ 2 private EnglishPlug englishPlug; 3 4 public IPhonePlugConverter(EnglishPlug englishPlug){ 5 this.englishPlug = englishPlug; 6 } 7 8 @Override 9 public void charge(){ 10 englishPlug.charge(); 11 } 12 }
现在需要写一个测试类来看看这个转换成到底能不能用:
1 public static void main(String[] args){ 2 ChinesePlug chinesePlug = new IPhonePlugConverter(new EnglishiPhonePlug()); 3 chinesePlug.charge(); //输出:英式插头在充电 4 }
可以看到,我们成功的用EnglishPlug“冒充”了ChinesePlug,实现了转换器的功能。当然,上面介绍的只是讲一个接口转换成另一个接口,实际上可以将一个接口转换为多个其他接口。
2. 外观模式
外观模式提供了简化接口的能力,将内部复杂的实现封装起来,开放给外部一个简单的接口,从而降低使用的复杂性。
以洗衣机为例,洗衣机有半自动和全自动两种,半自动洗衣机需要我们自己先注水,然后洗涤,放水,最后脱水,这几部需要我们手动一步一步完成,虽然可以做到精细化控制(例如可以自由选择注水多少,脱水几分钟),但是用起来不免有些麻烦,而全自动洗衣机只需要按下启动键就OK了,简单了许多。
下面是半自动洗衣机的定义:
1 public class SemiautoWashingMachine{ 2 public void addWater(){ 3 System.out.println("手动注水"); 4 } 5 6 public void washing(){ 7 System.out.println("开始洗衣服"); 8 } 9 10 public void drainage(){ 11 System.out.println("手动排水"); 12 } 13 14 public void dehydration(){ 15 System.out.println("手动脱水"); 16 } 17 }
用户使用半自动洗衣机,需要一步一步分别调用addWater(),washing(), drainage(), dehydration()这些接口。
全自动洗衣机定义如下:
1 public class FullautoWashingMachine{ 2 public void addWater(){ 3 System.out.println("手动注水"); 4 } 5 6 public void washing(){ 7 System.out.println("开始洗衣服"); 8 } 9 10 public void drainage(){ 11 System.out.println("手动排水"); 12 } 13 14 public void dehydration(){ 15 System.out.println("手动脱水"); 16 } 17 18 //对外提供的简单接口 19 public void simpleWashing(){ 20 this.addWater(); 21 this.washing(); 22 this.drainage(); 23 this.dehydration(); 24 } 25 }
用户使用全自动洗衣机,就只需要调用simyleWashing()就行了。
从上面可以看出来, 外观模式并没有增加新的功能,只是将原来的一些操作封装到一个接口里面,对外提供了一个简单的接口,避免让用户解除到底层,让系统更加容易使用。当然,如果你愿意,对象原来的一些底层功能(addWater()等方法)你仍然可以自由使用,像半自动洗衣机那样,每一步都手动来操作,只不过这样一来,用户就与对象的底层细节耦合在一起了。
3. 总结
总的来说,适配器模式提供了讲一个接口转换成其他接口的能力,而无需修改原来的接口;外观模式提供简化接口的能力,让使用者与接口内部的细节解耦,符合“最小知道原则”。
4. 参考
<<Head First设计模式>>