1. 概念
适配器模式是一种结构型设计模式, 它能使接口不兼容的对象能够相互合作。它和桥接模式有点像,都是连接两个不同维度的东西,但关键在于桥接模式是从设计之初就开始使用,而适配器模式更适用于遇到问题了再使用。
老样子,还是先从问题引入概念。假如你正在开发一款股票市场监测程序, 它会从不同来源下载 XML 格式的股票数据, 然后向用户呈现出美观的图表。
在开发过程中, 你决定在程序中整合一个第三方智能分析函数库。 但是遇到了一个问题, 那就是分析函数库只兼容 JSON 格式的数据。
你可以修改程序库来支持 XML。 但是, 这可能需要修改部分依赖该程序库的现有代码。 甚至还有更糟糕的情况, 你可能根本没有程序库的源代码, 从而无法对其进行修改。
这个时候适配器模式就来了,你可以创建一个适配器对象,实现接口的转换,以上面的例子来说,就是把XML的数据转换成Json格式的数据。这个具体该怎么实现呢?看下面代码。
适配器模式通过封装对象将复杂的转换过程隐藏于幕后。 被封装的对象甚至察觉不到适配器的存在。适配器不仅可以转换不同格式的数据, 其还有助于采用不同接口的对象之间的合作。 有时你甚至可以创建一个双向适配器来实现双向转换调用。它的运作方式如下:
- 适配器实现与其中一个现有对象兼容的接口。
- 现有对象可以使用该接口安全地调用适配器方法。
- 适配器方法被调用后将以另一个对象兼容的格式和顺序将请求传递给该对象。
适配器模式其实也很好理解,在现实世界中也可以找到很多类比,比如我们手机数据线和排查,数据线是没办法直接用到排查上的,必须借助插头,这里的插头就是适配器。
- 总结
- 适配器与桥接模式有点类似,但一个使用于解决实际问题,一个使用于一开始的架构设计。
- 适配器模式使用于解决两个维度之前有冲突的现象。
2. 特点
-
优点
- 单一职责原则,你可以将接口或数据转换代码从程序主要业务逻辑中分离。
- 开闭原则。 只要客户端代码通过客户端接口与适配器进行交互, 你就能在不修改现有客户端代码的情况下在程序中添加新类型的适配器。
-
缺点
- 代码整体复杂度增加, 因为你需要新增一系列接口和类。 有时直接更改服务类使其与其他代码兼容会更简单。
-
使用场景
-
当你希望使用某个类, 但是其接口与其他代码不兼容时, 可以使用适配器类。
适配器模式允许你创建一个中间层类, 其可作为代码与遗留类、 第三方类或提供怪异接口的类之间的转换器。
-
如果您需要复用这样一些类, 他们处于同一个继承体系, 并且他们又有了额外的一些共同的方法, 但是这些共同的方法不是所有在这一继承体系中的子类所具有的共性。
你可以扩展每个子类, 将缺少的功能添加到新的子类中。 但是, 你必须在所有新子类中重复添加这些代码, 这样会使得代码有坏味道。
将缺失功能添加到一个适配器类中是一种优雅得多的解决方案。 然后你可以将缺少功能的对象封装在适配器中, 从而动态地获取所需功能。 如要这一点正常运作, 目标类必须要有通用接口, 适配器的成员变量应当遵循该通用接口。 这种方式同装饰模式非常相似。所以适配器模式在某些角度,也和装饰模式很相似。
-
3. 实现
3.1 类适配器
-
UML类图
-
Java实现
-
被适配的类
/** * @Author: chy * @Description: 被适配的类:股票提供方 * @Date: Create in 11:29 2021/3/9 */ public class StockProvider { String getXML(){ return "XML数据"; } }
-
适配器接口
/** * @Author: chy * @Description: 适配器接口 * @Date: Create in 11:30 2021/3/9 */ public interface DataChange { // XML转JSON String dataChange(); }
-
适配器
/** * @Author: chy * @Description: 适配器类 * @Date: Create in 11:36 2021/3/9 */ public class Adapter extends StockProvider implements DataChange { @Override public String dataChange() { String XML = this.getXML(); return XML.replace("XML","JSON"); } }
-
使用者
/** * @Author: chy * @Description: 分析型数据库 * @Date: Create in 14:32 2021/3/9 */ public class AnalysisDatabase { void analysisData(String data){ if (data.equals("JSON")){ System.out.println("分析成功~~~"); } else { System.out.println("格式错误"); } } }
-
客户端
/** * @Author: chy * @Description: 客户端 * @Date: Create in 11:38 2021/3/9 */ public class Client { public static void main(String[] args) { // 新建适配器 Adapter adapter = new Adapter(); // 获得转化后的JSON数据 String JSON = adapter.dataChange(); // 新建数据库分析数据 AnalysisDatabase analysisDatabase = new AnalysisDatabase(); analysisDatabase.analysisData(JSON); } }
-
-
说明:这种模式采用继承被适配的类的方式,缺陷比较大,一般很少使用,因为直接大多数语言不支持多继承,所以只能支持适配一个类。
3.2 对象适配器
- UML类图
- Java实现
-
被适配的类
/** * @Author: chy * @Description: 被适配的类:股票提供方 * @Date: Create in 11:29 2021/3/9 */ public class StockProvider { String getXML(){ return "XML数据"; } }
-
适配器接口
/** * @Author: chy * @Description: 适配器接口 * @Date: Create in 11:30 2021/3/9 */ public interface DataChange { // XML转JSON String dataChange(); }
-
适配器
/** * @Author: chy * @Description: 适配器类 * @Date: Create in 11:36 2021/3/9 */ public class Adapter implements DataChange{ private StockProvider stockProvider; public Adapter(StockProvider stockProvider) { this.stockProvider = stockProvider; } @Override public String dataChange() { String XML = stockProvider.getXML(); return XML.replace("XML","JSON"); } }
-
使用者
/** * @Author: chy * @Description: 分析型数据库 * @Date: Create in 14:32 2021/3/9 */ public class AnalysisDatabase { void analysisData(String data){ if (data.equals("JSON")){ System.out.println("分析成功~~~"); } else { System.out.println("格式错误"); } } }
-
客户端
/** * @Author: chy * @Description: 客户端 * @Date: Create in 11:38 2021/3/9 */ public class Client { public static void main(String[] args) { // 新建适配器 Adapter adapter = new Adapter(new StockProvider()); // 获得转化后的JSON数据 String JSON = adapter.dataChange(); // 新建数据库分析数据 AnalysisDatabase analysisDatabase = new AnalysisDatabase(); analysisDatabase.analysisData(JSON); } }
-
- 说明: 这个才是真正比较常见的适配器使用方法——对象适配器,因为相比起第一种,它采用了聚合的方式,避免了类的单继承的限制,可以同时适配多种数据类型,比如它可以实现txt转JSON
3.3 接口适配器
-
UML类图
-
Java实现
-
被适配的类
/** * @Author: chy * @Description: 被适配的类:股票提供方 * @Date: Create in 11:29 2021/3/9 */ public class StockProvider { String getXML(){ return "XML"; } }
-
适配器接口
/** * @Author: chy * @Description: 数据转化接口 * @Date: Create in 14:56 2021/3/9 */ public interface DataChange { String XMLToJson(); String txtToJson(); }
-
适配器
/** * @Author: chy * @Description: 适配器类 * @Date: Create in 11:36 2021/3/9 */ public abstract class Adapter implements DataChange { @Override public String XMLToJson() { return null; } @Override public String txtToJson() { return null; } }
-
使用者
/** * @Author: chy * @Description: 分析型数据库 * @Date: Create in 14:32 2021/3/9 */ public class AnalysisDatabase { void analysisData(String data){ if (data.equals("JSON")){ System.out.println("分析成功~~~"); } else { System.out.println("格式错误"); } } }
-
客户端
/** * @Author: chy * @Description: 客户端 * @Date: Create in 11:38 2021/3/9 */ public class Client { public static void main(String[] args) { // 获取XML数据 StockProvider stockProvider = new StockProvider(); String xml = stockProvider.getXML(); // 匿名内部类重写适配器方法 Adapter adapter = new Adapter() { @Override public String XMLToJson() { return xml.replace("XML", "JSON"); } }; // 新建分析数据库,分析数据 AnalysisDatabase analysisDatabase = new AnalysisDatabase(); analysisDatabase.analysisData(adapter.XMLToJson()); } }
-
-
说明:这种模式很巧妙,适配器是抽象类,继承了适配器接口,然后对重写方法进行空实现,等实际用到适配器的时候再具体编写自己的适配逻辑。这样做十分灵活,而且具有很好的可扩展性,每个人都可以实现自己的定制方法,但也有缺点就是加强了客户端和其他类的耦合。