设计模式(五)适配器模式
适配器:
适配器这个东西,我想没什么人会感到陌生,平时我们手机充电的时候,连接插座和数据线的那个“插头”,就是我们口中的适配器。
为什么会需要这个东西呢?
这是由于产品的多样性造成的。在中国,每一个插座提供的电压为220V,而在其他国家,这个标准可能是110V,和中国有所不同。
而不是每个产品,在充电的时候,都需要220V的电压,有可能某些产品只需要110V,或者其他标准的电压,这种情况,一般称之为:不适配。
如果出现不适配的情况,强行充电,那么某些产品就可能一些不可预料的情况,例如,爆炸。
而适配器就是用来针对这种不适配情况的解决方案。
每一个适配器,都有一个输出和一个输入,将插座中获取的标准电压,转换成产品可以接受的特殊电压,保证充电的安全性。
这个输入和输出,也可能是一个范围。
适配器模式:
在开发的过程中,也许会遇见接口不兼容的问题,适配器模式就是这类问题的一种解决方案。
适配器模式分为两种:类适配器模式、对象适配器模式,前者是基于多继承实现的,在 Java 中不做讨论。
接口不兼容的具体表现为:某一个类,需要去实现某一个接口提供的行为,但是却没有能力做到这一点,这就是之前在适配器中提到的不兼容。
以上提到的,没有能力,具体指的是要做到这一点,会打破设计中的一些基本准则。
下面看另一个具体的现实场景,适配器模式的具体实现,会在之后的场景中体现。
现实场景:
在我们平时的日常中,翻译就是另一个典型的适配器模式应用。
现在假设这么一个场景:在球场上,教练制定战术,告诉球员。
教练制定战术所用的语言是英文,这就是一个标准(类比中国插座提供的220V电压)。
但是球员来自世界各地,不可能每一个人都懂英文(类比需要定制化电压的产品),这就产生了不适配的问题。
一般来说,以上的情况会怎么解决?
也许教练足够博学,他能够提供不止一套标准,也就是懂多国语言,可以将战术通过其他语言复述一遍,但是通常都不会这么做。
那就要每一个非英文母语球员,自己尝试去理解教练布置的战术,在这个过程中,需要一个翻译。
翻译就是这个场景中的适配器,他和其他英文母语的球员一样,拥有听懂教练布置战术的能力。
思想:
根据之前提供的解决方案,英文球员和翻译,都拥有相同的行为,所以会实现同一个接口,或者继承同一个抽象类。
非英文的球员(Adaptee),依赖翻译(Adapter),来实现理解战术的过程。
UML:
代码:
1 public interface IntroductionEN { 2 3 void attackEN(); 4 5 void defenseEN(); 6 7 }
1 @AllArgsConstructor 2 @Data 3 public class ManEN implements IntroductionEN { 4 5 private String name; 6 7 @Override 8 public void attackEN() { 9 System.out.println(this.name + " Attack"); 10 } 11 12 @Override 13 public void defenseEN() { 14 System.out.println(this.name + " Defense"); 15 } 16 17 }
1 @Data 2 @AllArgsConstructor 3 public class ManCN { 4 5 private String name; 6 7 public void attackCN() { 8 System.out.println(this.name + " 进攻"); 9 } 10 11 public void defenseCN() { 12 System.out.println(this.name + " 防守"); 13 } 14 15 }
1 @Data 2 @AllArgsConstructor 3 public class TranslatorEN implements IntroductionEN { 4 5 private ManCN manCN; 6 7 @Override 8 public void attackEN() { 9 manCN.attackCN(); 10 } 11 12 @Override 13 public void defenseEN() { 14 manCN.defenseCN(); 15 } 16 17 }
应用场景:
在编程世界里,不存在做不到这种事情,之所以使用适配器模式,更多的是为了防止代码打破开-闭原则。
所以,在设计代码的时候,一般不会考虑适配器模式,它更多的,是一种面对需求变更的补救措施。
适配器模式最多的应用场景,是为了适配第三方软件。
系统没有必要为了实现第三方提供的接口,而对自己的代码进行大的修改,这是就需要一个专门的适配器,去适应这些接口了。