目录
- 适配器模式
- 适配器模式结构
- 适配器模式适用场景
- 适配器模式优缺点
- 练手题目
- 题目描述
- 输入描述
- 输出描述
- 题解
适配器模式
适配器模式是一种结构型设计模式, 它能使接口不兼容的对象能够相互合作。
适配器模式结构
对象适配器
实现时使用了构成原则: 适配器实现了其中一个对象的接口, 并对另一个对象进行封装。 所有流行的编程语言都可以实现适配器。
-
客户端 (Client) 是包含当前程序业务逻辑的类。
-
客户端接口 (Client Interface) 描述了其他类与客户端代码合作时必须遵循的协议。
-
服务 (Service) 中有一些功能类 (通常来自第三方或遗留系统)。 客户端与其接口不兼容, 因此无法直接调用其功能。
-
适配器 (Adapter) 是一个可以同时与客户端和服务交互的类: 它在实现客户端接口的同时封装了服务对象。 适配器接受客户端通过适配器接口发起的调用, 并将其转换为适用于被封装服务对象的调用。
-
客户端代码只需通过接口与适配器交互即可, 无需与具体的适配器类耦合。 因此, 你可以向程序中添加新类型的适配器而无需修改已有代码。 这在服务类的接口被更改或替换时很有用: 你无需修改客户端代码就可以创建新的适配器类。
对象适配器模式通用代码:
// 目标接口
interface Target {void request();
}// 被适配的类
class Adaptee {public void specificRequest() {System.out.println("Adaptee's specific request");}
}// 适配器类
class Adapter implements Target {private Adaptee adaptee;public Adapter(Adaptee adaptee) {this.adaptee = adaptee;}@Overridepublic void request() {adaptee.specificRequest();}
}// 客户端代码
public class Client {public static void main(String[] args) {Adaptee adaptee = new Adaptee();Target target = new Adapter(adaptee);target.request();}
}
类适配器
这一实现使用了继承机制: 适配器同时继承两个对象的接口。 请注意, 这种方式仅能在支持多重继承的编程语言中实现, 例如 C++。但是Java不支持多继承,所以使用Java时,对象适配器使用场景相对更多。
类适配器模式通用代码:
// 目标接口
interface Target {void request();
}// 被适配的类
class Adaptee {public void specificRequest() {System.out.println("Adaptee's specific request");}
}// 适配器类
class Adapter extends Adaptee implements Target {@Overridepublic void request() {specificRequest();}
}// 客户端代码
public class Client {public static void main(String[] args) {Target target = new Adapter();target.request();}
}
类适配器与对象适配器的主要区别在于:
-
类适配器使用继承,而对象适配器使用组合。
-
类适配器可以重写
Adaptee
的方法,而对象适配器不能。 -
类适配器不需要额外的对象来进行适配,可能在某些情况下更高效。
-
类适配器只能适配一个类,而对象适配器可以适配多个类。
适配器模式适用场景
-
当你希望使用某个类, 但是其接口与其他代码不兼容时, 可以使用适配器类。
适配器模式允许你创建一个中间层类, 其可作为代码与遗留类、 第三方类或提供怪异接口的类之间的转换器。
-
如果您需要复用这样一些类, 他们处于同一个继承体系, 并且他们又有了额外的一些共同的方法, 但是这些共同的方法不是所有在这一继承体系中的子类所具有的共性。
你可以扩展每个子类, 将缺少的功能添加到新的子类中。 但是, 你必须在所有新子类中重复添加这些代码, 这样会使得代码有坏味道。
将缺失功能添加到一个适配器类中是一种优雅得多的解决方案。 然后你可以将缺少功能的对象封装在适配器中, 从而动态地获取所需功能。 如要这一点正常运作, 目标类必须要有通用接口, 适配器的成员变量应当遵循该通用接口。
识别方法: 适配器可以通过以不同抽象或接口类型实例为参数的构造函数来识别。 当适配器的任何方法被调用时, 它会将参数转换为合适的格式, 然后将调用定向到其封装对象中的一个或多个方法。
适配器模式优缺点
适配器模式优点:
-
单一职责原则你可以将接口或数据转换代码从程序主要业务逻辑中分离。
-
开闭原则。 只要客户端代码通过客户端接口与适配器进行交互, 你就能在不修改现有客户端代码的情况下在程序中添加新类型的适配器。
适配器模式缺点:
- 代码整体复杂度增加, 因为你需要新增一系列接口和类。 有时直接更改服务类使其与其他代码兼容会更简单。
练手题目
题目描述
小明购买了一台新电脑,该电脑使用 TypeC 接口,他已经有了一个USB接口的充电器和数据线,为了确保新电脑可以使用现有的USB接口充电器和数据线,他购买了一个TypeC到USB的扩展坞。
请你使用适配器模式设计并实现这个扩展坞系统,确保小明的新电脑既可以通过扩展坞使用现有的USB接口充电线和数据线,也可以使用TypeC接口充电。
输入描述
题目包含多行输入,第一行输入一个数字 N (1 < N <= 20),表示后面有N组测试数据。
之后N行都是一个整数,1表示使用电脑本身的TypeC接口,2表示使用扩展坞的USB接口充电。
输出描述
根据每行输入,输出相应的充电信息。
题解
类适配器模式代码:
import java.util.*;// 定义计算机端口接口
interface ComputerPort {void connect();
}// TypeC端口类实现ComputerPort接口
class TypeCPort implements ComputerPort {@Overridepublic void connect() {System.out.println("TypeC");}
}// USB设备类
class USBDevice {public void connectUSB() {System.out.println("USB Adapter");}
}// TypeC到USB适配器类
class TypeCToUSBAdapter extends USBDevice implements ComputerPort {@Overridepublic void connect() {super.connectUSB();}
}public class Main{public static void main(String[] args) {Scanner inputScanner = new Scanner(System.in);Map<Integer, ComputerPort> connectionModes = new HashMap<>();connectionModes.put(1, new TypeCPort());connectionModes.put(2, new TypeCToUSBAdapter());int totalConnections = inputScanner.nextInt();inputScanner.nextLine();while (inputScanner.hasNextInt()) {int choice = inputScanner.nextInt();connectionModes.getOrDefault(choice, () -> System.out.println("")).connect();}inputScanner.close();}
}
对象适配器模式:
import java.util.*;// 定义电脑端口接口
interface ComputerPort {void connect();
}// TypeC端口实现
class TypeCPort implements ComputerPort {@Overridepublic void connect() {System.out.println("TypeC");}
}// USB设备类
class USBDevice {public void connectUSB() {System.out.println("USB Adapter");}
}// TypeC到USB的扩展坞(适配器)
class TypeCToUSBHub implements ComputerPort {private USBDevice usbDevice;public TypeCToUSBHub(USBDevice usbDevice) {this.usbDevice = usbDevice;}@Overridepublic void connect() {usbDevice.connectUSB(); }
}public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);// 创建连接映射,用于存储不同类型的连接方式Map<Integer, ComputerPort> connections = new HashMap<>();connections.put(1, new TypeCPort());connections.put(2, new TypeCToUSBHub(new USBDevice()));scanner.nextInt(); scanner.nextLine(); while (scanner.hasNextInt()) {int choice = scanner.nextInt();ComputerPort connection = connections.get(choice);if (connection != null) {connection.connect(); } else {System.out.println("无效的选择"); }}scanner.close(); }
}