1 基础知识
定义:将一个类的接口(被适配者)转换成客户期望的另一个接口(目标)。特征:使原本接口不兼容的类可以一起工作。
本质:转换匹配,复用功能。把不兼容的接口转换为客户端期望的样子从而实现功能的复用。
使用场景:已经存在的类,它的方法(接口)和需求不匹配时的解决方案。注意适配器模式不是软件设计阶段需要考虑的设计模式,而是随着软件维护,由于不同产品不同厂家造成功能类似而接口不相同情况下的解决方案。
优点:
更好的复用:性如果功能是已经有了的,只是接口不兼容,那么通过适配器模式就可以让这些功能得到更好的复用。
更好的可扩展性:在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。
2 代码示例
适配器的两种实现方式:
类适配器:
被适配者类:Adaptee
public class Adaptee { public void adapteeRequest(){ System.out.println("被适配者的方法"); } }
目标接口:Target
public interface Target { void request(); }
目标实现:
public class ConcreteTarget implements Target { @Override public void request() { System.out.println("concreteTarget目标方法"); } }
适配者类:Adapter 通过继承和实现将被适配者类和目标联系在了一起
public class Adapter extends Adaptee implements Target{ @Override public void request() { //在此方法体内可以添加需要的代码 super.adapteeRequest(); } }
应用层:
public class Test { public static void main(String[] args) { Target target = new ConcreteTarget(); //目标方法输出 target.request(); Target adapterTarget = new Adapter(); //适配者方法输出 adapterTarget.request(); } }
其类图关系如下:
对象适配器:
修改适配者类:
public class Adapter implements Target{ //通过组合方式而不是继承 private Adaptee adaptee = new Adaptee(); @Override public void request() { //添加自己需要代码 adaptee.adapteeRequest(); } }
应用层代码不变直接测试即可,其类关系图如下图,在代码中有一个原则如果继承和组合都可以满足要求优先选择组合。
场景使用:生活中的手机充电,需要把220V转换为5V电源。
220V电源:被适配者
public class AC220 { public int outputAC220V(){ int output = 220; System.out.println("输出交流电"+output+"V"); return output; } }
目标接口:5V电源
public interface DC5 { int outputDC5V(); }
适配者:
public class PowerAdapter implements DC5{ private AC220 ac220 = new AC220(); @Override public int outputDC5V() { int adapterInput = ac220.outputAC220V(); //变压器... int adapterOutput = adapterInput/44; System.out.println("使用PowerAdapter输入AC:"+adapterInput+"V"+"输出DC:"+adapterOutput+"V"); return adapterOutput; } }
应用层:
public class Test { public static void main(String[] args) { DC5 dc5 = new PowerAdapter(); dc5.outputDC5V(); } }
在实际开发中场景:如一个日志记录系统最初的的版本1:LogMode1 只有两个简单的功能以文件的形式读日志和写日志。
public interface LogMode1 { public void writeLogFile(); public void readLogFile(); }
随着使用升级系统便有了版本2:将日志保存到数据库中可以对其进行增删改查
public interface LogMode12 { //新增日志 public void creatLogFile(); //修改日志 public void updateLogFile(); //删除日志 public void removeLogFile(); //获取所有日志 public List<LogFile> getLogFile(); }
此时客户端提出需求,能否让版本2同时支持数据库存储和文件存储?直接的思路是进行合并但是有问题,现在客户端操作的第二版的日志接口,但第一版的接口与第二版的不同,客户端无法以同样的方式使用第一版的实现,如下图所示:
简单粗暴的方法是在第二版中增加文件存储功能,但是这些功能在第一版中已经实现过了,因此要避免重复劳动。解决方法就是采用适配器模式:Target接口相当于第二版的日志接口,被适配者则是第一版接口,采用类似前面提供的代码即可解决。
3 源代码中的使用
4 相关设计模式
(1)适配器模式与桥接模式
其实这两个模式除了结构略为相似外,功能上完全不同。适配器模式是把两个或者多个接口的功能进行转换匹配;而桥接模式是让接口和实现部分相分离,以便它们可以相对独立地变化。
(2)适配器模式与装饰模式
(3)适配器模式和代模式
适配器模式可以和代理模式组合使用。在实现近配器的时候,可以通过代理来调用 Adaptee,这样可以获得更大的灵活性。
(4)适配器模式和抽象工厂模式。
在适配器实现的时候,通常需要得到被适配的对象。如果被适配的是一个接口,那么就可以结合一些可以创造对象实例的设计模式,来得到被适配的对象示例,比如抽象工厂模式、单例模式、工厂方法模式等。
0