适配器模式和外观模式

      适配器模式提供了将一种对象转换成另一种对象的能力,利用它可以实现两个不兼容接口的协调工作。外观模式是封装对象内的复杂逻辑,对外提供一个简化的接口。

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设计模式>>

posted @ 2019-09-14 02:01  纳兰小依  阅读(858)  评论(0编辑  收藏  举报