1. 意图
将一个类的接口装换成客户希望的另外一个接口
2. 动机
有时,为复用而设计的工具类不能够被复用仅仅是因为它的接口与专业应用领域所需要的接口不匹配。Adapter经常还要负责提供那些被匹配的类所没有提供的功能(有点类似装饰模式)
3. 适用性
- 想使用一个已经存在的类,而它的接口不符合你的需求
- 想要创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作
- 复用一些类,它们处于同一继承体系,并且又有了一些共同的方法,但是这些共同的方法不是所有这一继承体系中的子类所共有的。不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口
4. 结构
5. 效果
1) 允许一个Adapter与多个Adaptee——Adaptee本身以及它的所有子类——同时工作。Adapter可以一次给所有的Adaptee添加功能
2) 使得重定义Adapter的行为比较困难。这就需要生成Adaptee的子类,并且使得Adapter引用这个子类而不是Adaptee本身
3) 单一职责原则_你可以将接口或数据转换代码从程序主要业务逻辑中分离
4) 开闭原则。 只要客户端代码通过客户端接口与适配器进行交互, 你就能在不修改现有客户端代码的情况下在程序中添加新类型的适配器
6. 代码实现
让方钉适配圆孔
round/RoundHole.java: 圆孔
package adapter.round; /** * @author GaoMing * @date 2021/7/12 - 9:07 * RoundHoles are compatible with RoundPegs. */ public class RoundHole { private double radius; public RoundHole(double radius) { this.radius = radius; } public double getRadius() { return radius; } public boolean fits(RoundPeg peg) { boolean result; result = (this.getRadius() >= peg.getRadius()); return result; } }
round/RoundPeg.java: 圆钉
package adapter.round; /** * @author GaoMing * @date 2021/7/12 - 9:07 */ public class RoundPeg { private double radius; public RoundPeg() {} public RoundPeg(double radius) { this.radius = radius; } public double getRadius() { return radius; } }
square/SquarePeg.java: 方钉
package adapter.square; /** * @author GaoMing * @date 2021/7/12 - 9:08 * SquarePegs are not compatible with RoundHoles (they were implemented by * previous development team). But we have to integrate them into our program. */ public class SquarePeg { private double width; public SquarePeg(double width) { this.width = width; } public double getWidth() { return width; } public double getSquare() { double result; result = Math.pow(this.width, 2); return result; } }
adapters/SquarePegAdapter.java: 方钉到圆孔的适配器
package adapter.adapters; import adapter.round.RoundPeg; import adapter.square.SquarePeg; /** * @author GaoMing * @date 2021/7/12 - 9:25 * Adapter allows fitting square pegs into round holes. */ public class SquarePegAdapter extends RoundPeg { private SquarePeg peg; public SquarePegAdapter(SquarePeg peg) { this.peg = peg; } @Override public double getRadius() { double result; // Calculate a minimum circle radius, which can fit this peg. result = (Math.sqrt(Math.pow((peg.getWidth() / 2), 2) * 2)); return result; } }
Demo.java: 客户端代码
package adapter; import adapter.adapters.SquarePegAdapter; import adapter.round.RoundHole; import adapter.round.RoundPeg; import adapter.square.SquarePeg; /** * @author GaoMing * @date 2021/7/12 - 9:07 */ public class Demo { public static void main(String[] args) { // Round fits round, no surprise. RoundHole hole = new RoundHole(5); RoundPeg rpeg = new RoundPeg(5); if (hole.fits(rpeg)) { System.out.println("Round peg r5 fits round hole r5."); } SquarePeg smallSqPeg = new SquarePeg(2); SquarePeg largeSqPeg = new SquarePeg(20); // hole.fits(smallSqPeg); // Won't compile. // Adapter solves the problem. SquarePegAdapter smallSqPegAdapter = new SquarePegAdapter(smallSqPeg); SquarePegAdapter largeSqPegAdapter = new SquarePegAdapter(largeSqPeg); if (hole.fits(smallSqPegAdapter)) { System.out.println("Square peg w2 fits round hole r5."); } if (!hole.fits(largeSqPegAdapter)) { System.out.println("Square peg w20 does not fit into round hole r5."); } } }
执行结果
Round peg r5 fits round hole r5. Square peg w2 fits round hole r5. Square peg w20 does not fit into round hole r5.
7. 与其他模式的关系
- 桥接模式通常会于开发前期进行设计, 使你能够将程序的各个部分独立开来以便开发。 另一方面, 适配器模式通常在已有程序中使用, 让相互不兼容的类能很好地合作
- 适配器可以对已有对象的接口进行修改, 装饰模式则能在不改变对象接口的前提下强化对象功能。 此外, 装饰还支持递归组合, 适配器则无法实现
- 适配器能为被封装对象提供不同的接口, 代理模式能为对象提供相同的接口, 装饰则能为对象提供加强的接口
- 外观模式为现有对象定义了一个新接口, 适配器则会试图运用已有的接口。 适配器通常只封装一个对象, 外观通常会作用于整个对象子系统上
8. 已知应用
适配器模式在 Java 代码中很常见。 基于一些遗留代码的系统常常会使用该模式。 在这种情况下, 适配器让遗留代码与现代的类得以相互合作
- java.util.Arrays#asList()
- java.util.Collections#list()
- java.util.Collections#enumeration()
- java.io.InputStreamReader(InputStream) (返回 Reader对象)
- java.io.OutputStreamWriter(OutputStream) (返回 Writer对象)
- javax.xml.bind.annotation.adapters.XmlAdapter#marshal() 和 #unmarshal()
识别方法: 适配器可以通过以不同抽象或接口类型实例为参数的构造函数来识别。 当适配器的任何方法被调用时, 它会将参数转换为合适的格式, 然后将调用定向到其封装对象中的一个或多个方法