什么是适配器
适配器帮助我们使两个不兼容的接口兼容。这到底是什么意思呢?如果我们有一个旧的组件,我们想在一个新的系统中使用它,或者我们想在一个旧的系统中使用一个新的组件。两者很少能在不需要修改代码的情况下进行交流。
但是,改变代码并不总是可能的,要么是因为我们无法访问它,要么是因为它不切实际。在这种情况下,我们可以写一个额外的层,对两个接口之间的通信进行一些必要的修改,在两个接口之间实现通信。
这个层被称为适配器。
适配器的作用
适配器不仅可以转换不同格式的数据, 其还有助于采用不同接口的对象之间的合作。 它的运作方式如下:
- 适配器实现与其中一个现有对象兼容的接口。
- 现有对象可以使用该接口安全地调用适配器方法。
- 适配器方法被调用后将以另一个对象兼容的格式和顺序将请求传递给该对象。
有时你甚至可以创建一个双向适配器来实现双向转换调用。
真实世界的例子
不同的电子产品的适配接口也不一样,比如相机和手机的接口不同。当你使用不同的电子产品,需要不同读取数据的接口:USB、HDMI、VGA 等接口。
适配器模式结构
对象适配器
当我们的 client 想要打开风扇,而风扇提供了 turnOn
方法,直接调用 turnOn
即可享受凉爽的风,比如下图:
然后同样是打开的操作,client 想通过 turnOn
调用打开电视机,而电视机没有这个方法,电视机提供的 open
方法。
如果我们直接把 open
方法改为 turnOn
的方法,可能会引发系统的一系列其他问题,这个时候就需要我们的适配器模式了。
客户端可以会使用各种各样的适配器,为了方便保存,可以为适配器抽象出一个接口,UML 图如下:
适配器 (Adapter) 是一个可以同时与客户端和服务交互的类: 它在实现客户端接口的同时封装了服务对象。 适配器接受客户端通过适配器接口发起的调用, 并将其转换为适用于被封装服务对象的调用。
客户端代码只需通过接口与适配器交互即可, 无需与具体的适配器类耦合。 因此, 你可以向程序中添加新类型的适配器而无需修改已有代码。 这在服务类的接口被更改或替换时很有用: 你无需修改客户端代码就可以创建新的适配器类。
代码如下:
public interface IAdapter { public void turnOn(); // 通用开机函数 } public class Tv { public void open() { System.out.println("电视机 TV 开机,只能使用 open 函数"); } } public class TvAdapter extends Tv implements IAdapter { public void turnOn() { super.open(); // 适配器模式 } }
调用方式:
public class Main { public static void main(String[] args) { IAdapter adapter = new TvAdapter(); adapter.turnOn(); } }
类适配器
这一实现使用了继承机制: 适配器同时继承两个对象的接口。 请注意, 这种方式仅能在支持多重继承的编程语言中实现, 例如 C++。
Python 实现
class Target: """ The Target defines the domain-specific interface used by the client code. """ def request(self) -> str: return "Target: The default target's behavior." class Adaptee: """ The Adaptee contains some useful behavior, but its interface is incompatible with the existing client code. The Adaptee needs some adaptation before the client code can use it. """ def specific_request(self) -> str: return ".eetpadA eht fo roivaheb laicepS" class Adapter(Target, Adaptee): """ The Adapter makes the Adaptee's interface compatible with the Target's interface via multiple inheritance. """ def request(self) -> str: return f"Adapter: (TRANSLATED) {self.specific_request()[::-1]}" def client_code(target: "Target") -> None: """ The client code supports all classes that follow the Target interface. """ print(target.request(), end="") if __name__ == "__main__": print("Client: I can work just fine with the Target objects:") target = Target() client_code(target) print("\n") adaptee = Adaptee() print("Client: The Adaptee class has a weird interface. " "See, I don't understand it:") print(f"Adaptee: {adaptee.specific_request()}", end="\n\n") print("Client: But I can work with it via the Adapter:") adapter = Adapter() client_code(adapter)
运行该程序:
Client: I can work just fine with the Target objects: Target: The default target's behavior. Client: The Adaptee class has a weird interface. See, I don't understand it: Adaptee: .eetpadA eht fo roivaheb laicepS Client: But I can work with it via the Adapter: Adapter: (TRANSLATED) Special behavior of the Adaptee.
适配器模式优缺点
优点:
- 单一职责原则你可以将接口或数据转换代码从程序主要业务逻辑中分离。
- 开闭原则。 只要客户端代码通过客户端接口与适配器进行交互, 你就能在不修改现有客户端代码的情况下在程序中添加新类型的适配器。
缺点:
- 代码整体复杂度增加, 因为你需要新增一系列接口和类。 有时直接更改服务类使其与其他代码兼容会更简单。