案例分析:设计模式与代码的结构特性-适配器模式
适配器模式的定义
适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。在适配器模式中,存在两种不同的模式结构:类的适配器模式和对象的适配器模式。
声明:java并不支持多重继承,及一个类只能有一个父类,本文给出的适配器模式的类图按照GOF著作分类属于对象适配器模式的类图,而不是类适配器的类图。
应用场景中的适用性
假设有一个洗衣机使用三孔插头,还有一个电视机使用双孔插头。
1、Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
2、Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法。
3、Adapter(适配器类):通过包装一个需要适配的对象,把原接口转换成目标接口。
关键代码
package com.AdapterPattern; public abstract interface ThreeElectricOutlet { public abstract void connectElectricCurrent(); } package com.AdapterPattern; public abstract interface TwoElectricOutlet { public abstract void connectElectricCurrent(); } package com.AdapterPattern; public class ThreeElectricAdapter implements ThreeElectricOutlet { private TwoElectricOutlet twoElectricOutlet; public ThreeElectricAdapter(TwoElectricOutlet twoElectricOutlet) { super(); this.twoElectricOutlet = twoElectricOutlet; } public void connectElectricCurrent() { twoElectricOutlet.connectElectricCurrent(); } } package com.AdapterPattern; public class Application { public static void main(String[] args) { ThreeElectricOutlet outlet; //目标接口(三相接口) Wash wash=new Wash(); //洗衣机 outlet=wash; //洗衣机插在三相插座上 System.out.println("使用三相插座接通电流:"); outlet.connectElectricCurrent(); //接通电流,开始洗衣服 TV tv=new TV(); //电视机 ThreeElectricAdapter adapter=new ThreeElectricAdapter(tv); //把电视机插在适配器上 outlet=adapter; //适配器插在三相插座上 System.out.println("使用三相插座接通电流:"); outlet.connectElectricCurrent(); //接通电流,开始播放节目 } } class Wash implements ThreeElectricOutlet{ //洗衣机使用三相插座 String name; public Wash() { name="黄河洗衣机"; } public Wash(String name) { this.name = name; } public void connectElectricCurrent() { turnOn(); } public void turnOn() { System.out.println(name+"开始洗衣物。"); } } class TV implements TwoElectricOutlet { //电视机使用两相插座 String name; public TV() { name="长江电视机"; } public TV(String name) { this.name = name; } public void connectElectricCurrent() { turnOn(); } public void turnOn() { System.out.println(name+"开始播放节目。"); } }
双向适配器
在对象适配器模式中,如果Adapter角色同时实现目标接口和适配器接口,并包含目标和被适配者的引用,那么该适配器就是一个双向适配器。使用双向适配器,用户既可以使用新的接口也可以使用已有的接口。
例如:如果用户希望能用新的三相插座来为洗衣机和电视机接通电流,也能用旧的两相插座为洗衣机接通电流,那么就必须使用一个双向适配器。
双向适配器的代码如下:
package com.AdapterPattern; public class ThreeAndTwoElectricAdapter implements ThreeElectricOutlet,TwoElectricOutlet{ TwoElectricOutlet twoElectricOutlet; ThreeElectricOutlet threeElectricOutlet; public ThreeAndTwoElectricAdapter(TwoElectricOutlet twoElectricOutlet, ThreeElectricOutlet threeElectricOutlet) { super(); this.twoElectricOutlet = twoElectricOutlet; this.threeElectricOutlet = threeElectricOutlet; } public ThreeAndTwoElectricAdapter(ThreeElectricOutlet threeElectricOutlet,TwoElectricOutlet twoElectricOutlet ) { super(); this.twoElectricOutlet = twoElectricOutlet; this.threeElectricOutlet = threeElectricOutlet; } public void connectElectricCurrent() { if(this instanceof ThreeElectricOutlet) twoElectricOutlet.connectElectricCurrent(); //TwoElectricOutlet是被适配接口 if(this instanceof TwoElectricOutlet) threeElectricOutlet.connectElectricCurrent(); //threeElectricOutlet是被适配接口 } }
适配器模式带来的好处
-
将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。
-
增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用。
-
灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。
多态
两个电器一个使用双孔插座,一个使用三孔插座,分别使用了接口多态:ThreeElectricOutlet和TwoElectricOutlet
封装
代码中所有的方法均为public(所有类可见),电器中的name变量为默认,即本包可以访问。没有实现封装。我们可以以洗衣机为例,按照以下方法封装。
class Wash implements ThreeElectricOutlet{ //洗衣机使用三相插座 private String name; public String getname(){ return name; } public Wash() { name="黄河洗衣机"; } public Wash(String name) { this.name = name; } public void connectElectricCurrent() { turnOn(); } public void turnOn() { System.out.println(name+"开始洗衣物。"); } }
从而实现了对于变量的保护。加强了程式码的安全性。
耦合度与内聚度
该代码主要是为了帮助理解适配器模式的意义,所以较为简单。
对于内聚度来说,代码较为简单,两个电器也不是一起工作为了实现某个功能,所以体现不是特别明显。
对于耦合度来说,首先是使用了接口,使得耦合度降低。
其次由于引入了适配器,目标类和适配者之间的耦合度降低。也就是说模块之间的联系变得简单。如果适配者发生变化,修改适配器,目标类并不会收到影响。
部署的操作过程
测试代码
package com.AdapterPattern; public class BiApplication { public static void main(String[] args) { ThreeElectricOutlet threeElectricOutlet; //ThreeElectricOutlet接口(三相插座 TwoElectricOutlet twoElectricOutlet; //TwoElectricOutlet接口(两相插座) Wash wash=new Wash(); //洗衣机 TV tv=new TV(); //电视机 ThreeAndTwoElectricAdapter adapter=new ThreeAndTwoElectricAdapter(wash, tv); //双向适配器 threeElectricOutlet=adapter; //适配器插在三相插座上 System.out.println("使用三相插座接通电源:"); threeElectricOutlet.connectElectricCurrent(); twoElectricOutlet=adapter; //适配器插在两相插座上 System.out.println("使用两相插座接通电源"); twoElectricOutlet.connectElectricCurrent(); } }
源代码地址:
https://github.com/androidwolf/designpattern--adapter