适配器模式

220 阅读4分钟

1. 概述

适配器模式是一种结构性设计模式,它能使接口不兼容的对象能够相互合作

2. 问题

假如你正在开发一款股票市场监测程序, 它会从不同来源下载 XML 格式的股票数据, 然后向用户呈现出美观的图表。

在开发过程中, 你决定在程序中整合一个第三方智能分析函数库。 但是遇到了一个问题, 那就是分析函数库只兼容 JSON 格式的数据。

image-20240612202506206

你无法 “直接” 使用分析函数库, 因为它所需的输入数据格式与你的程序不兼容

你可以修改程序库来支持 XML。 但是, 这可能需要修改部分依赖该程序库的现有代码。 甚至还有更糟糕的情况, 你可能根本没有程序库的源代码, 从而无法对其进行修改。

3. 解决方案

你可以创建一个适配器。 这是一个特殊的对象, 能够转换对象接口, 使其能与其他对象进行交互。

适配器模式通过封装对象将复杂的转换过程隐藏于幕后,被封装的对象甚至察觉不到适配器的存在。

适配器不仅可以转换不同格式的数据,其还有助于采用不同接口的对象之间的合作,它的运作方式如下:

  1. 适配器实现与其中一个现有对象兼容的接口
  2. 现有对象可以使用该接口安全地调用适配器方法
  3. 适配器方法被调用后将以另一个对象兼容的格式和顺序将请求传递给对象

甚至还可以创建双向适配器

image-20240612202925501

4. 适配器模式结构

4.1 对象适配器

适配器实现了其中一个对象的接口,并对另一个对象进行封装。

image-20240612203715536

 // 目标接口,需要被适配的接口
 interface Target {
     void request();
 }
 ​
 // 需要被适配的类
 class Adaptee {
     void specificRequest() {
         System.out.println("Adaptee's specific request");
     }
 }
 ​
 // 对象适配器类,实现了目标接口,并持有一个被适配类的实例
 class ObjectAdapter implements Target {
     private Adaptee adaptee;
 ​
     ObjectAdapter(Adaptee adaptee) {
         this.adaptee = adaptee;
     }
 ​
     @Override
     public void request() {
         adaptee.specificRequest();
     }
 }
 ​
 // 客户端代码
 public class Client {
     public static void main(String[] args) {
         Adaptee adaptee = new Adaptee();
         Target adapter = new ObjectAdapter(adaptee);
 ​
         adapter.request();
     }
 }

4.2 类适配器

适配器同时继承两个对象类的接口。

请注意,这种方式仅能在支持多种继承的编程语言中实现。例如c++

image-20240612204203751

5. 使用场景

  • 当你希望使用某个类,但是其接口与其他代码不兼容时,可以使用适配器类

6. 实现方式以及代码实例

实现方式:

适配器模式的实现方式通常包括以下步骤:

  1. 识别接口不兼容的类: 首先确定哪些类的接口不兼容,即它们无法直接交互。
  2. 创建适配器类: 创建一个新的适配器类,该类将兼容两个不兼容类的接口。
  3. 实现适配器类: 在适配器类中实现兼容两个类的接口,通常需要使用一个类的实例作为另一个类的成员来实现适配。
  4. 使用适配器: 将适配器类用于使原本不兼容的类能够一起工作。

基于经典的 “方钉和圆孔” 问题。

image-20240612204644128

 // 圆孔类
 class RoundHole {
     private double radius;
 ​
     // 构造函数,传入圆孔的半径
     RoundHole(double radius) {
         this.radius = radius;
     }
 ​
     // 返回圆孔的半径
     double getRadius() {
         return radius;
     }
 ​
     // 判断一个圆钉是否适配圆孔
     boolean fits(RoundPeg peg) {
         return this.getRadius() >= peg.getRadius();
     }
 }
 ​
 // 圆钉类
 class RoundPeg {
     private double radius;
 ​
     // 构造函数,传入圆钉的半径
     RoundPeg(double radius) {
         this.radius = radius;
     }
 ​
     // 返回圆钉的半径
     double getRadius() {
         return radius;
     }
 }
 ​
 // 方钉类
 class SquarePeg {
     private double width;
 ​
     // 构造函数,传入方钉的宽度
     SquarePeg(double width) {
         this.width = width;
     }
 ​
     // 返回方钉的宽度
     double getWidth() {
         return width;
     }
 }
 ​
 // 适配器类,使得方钉能够适配圆孔
 class SquarePegAdapter extends RoundPeg {
     private SquarePeg peg;
 ​
     // 构造函数,接收一个 SquarePeg 对象
     SquarePegAdapter(SquarePeg peg) {
         // 在实际情况中,适配器中会包含一个 SquarePeg 类的实例。
         this.peg = peg;
     }
 ​
     // 返回伪装成圆钉的方钉的半径
     @Override
     double getRadius() {
         // 适配器会假扮为一个圆钉,其半径刚好能与适配器实际封装的方钉搭配起来。
         return peg.getWidth() * Math.sqrt(2) / 2;
     }
 }
 ​
 // 客户端代码
 public class Client {
     public static void main(String[] args) {
         // 创建圆孔对象,传入半径为 5
         RoundHole hole = new RoundHole(5);
         // 创建圆钉对象,传入半径为 5
         RoundPeg rpeg = new RoundPeg(5);
         // 判断圆钉是否适配圆孔
         System.out.println(hole.fits(rpeg)); // true
 ​
         // 创建方钉对象,传入宽度为 5
         SquarePeg small_sqpeg = new SquarePeg(5);
         // 创建方钉适配器,传入方钉对象
         SquarePegAdapter small_sqpeg_adapter = new SquarePegAdapter(small_sqpeg);
         // 判断方钉适配器是否适配圆孔
         System.out.println(hole.fits(small_sqpeg_adapter)); // true
 ​
         // 创建方钉对象,传入宽度为 10
         SquarePeg large_sqpeg = new SquarePeg(10);
         // 创建方钉适配器,传入方钉对象
         SquarePegAdapter large_sqpeg_adapter = new SquarePegAdapter(large_sqpeg);
         // 判断方钉适配器是否适配圆孔
         System.out.println(hole.fits(large_sqpeg_adapter)); // false
     }
 }