适配器模式
简介
适配器模式(Adapter Pattern)是指将一个类的接口转换成客户端希望的另一个接口,使原本因接口不兼容而不能一起工作的类可以一起工作。
角色组成
- Target (目标接口): 定义了客户端代码所使用的特定领域接口。这是客户端期望与之交互的接口。
- Client (客户端): 与符合 Target 接口的对象进行交互。客户端不知道也不关心它实际使用的是不是一个适配器。
- Adaptee (被适配者): 一个已经存在的类,但它的接口与 Target 接口不兼容。这是需要被适配的类。
- Adapter (适配器): 实现 Target 接口,并持有 Adaptee 的一个实例(对象适配器)或继承自 Adaptee 和 Target(类适配器,在 Python 中不常用)。它负责将来自客户端对 Target 接 口的调用转换成对 Adaptee 接口的调用。
两种主要形式:
- 对象适配器 (Object Adapter): 适配器持有(组合)一个被适配者(Adaptee)的实例。这是更常用、更灵活的方式,尤其是在 Python 中,因为它优先使用组合而非继承。
- 类适配器 (Class Adapter): 适配器通过多重继承,同时继承 Target 接口(或类)和 Adaptee 类。Python 支持多重继承,但这种方式耦合度更高,不够灵活。
适用场景
- 使用一个已经存在的类,但其接口与你的需求不匹配时。 这是最典型的场景,比如集成第三方库或遗留代码。
- 希望创建一个可复用的类,该类可以与一些接口不兼容的类协同工作时。 适配器可以使这些不相关的类能够一起工作。
- 需要适配 Adaptee 的不同子类,但不想为每个子类都创建一个适配器时。 对象适配器可以通过持有 Adaptee 的父类引用来与 Adaptee 的所有子类工作。
- 当你无法修改 Adaptee 源代码,但又需要让它适配新的接口时。
优点
- 代码复用: 可以复用现有的 Adaptee 类,而无需修改其源代码。
- 解耦: 将客户端与具体的 Adaptee 实现解耦。客户端仅依赖于 Target 接口。
- 单一职责原则: 接口转换的逻辑被封装在适配器中,使得 Adaptee 和 Client 的代码保持简洁,职责单一 。
- 灵活性(对象适配器):
- 一个适配器可以适配 Adaptee 及其所有子类。
- 可以在将请求委派给 Adaptee 对象之前或之后轻松添加新功能。
缺点
- 增加系统复杂度: 引入了额外的适配器类,使得系统中的类数量增加,代码整体复杂度有所提高。
- 额外的代码: 需要编写额外的适配器代码来完成接口的转换。
- 可能掩盖问题: 过度使用适配器有时可能掩盖了系统设计本身的不合理性(例如,接口定义混乱)。
示例代码
打印机-对象适配器
假设我们有一个旧的 LegacyPrinter
类,它有一个 print_legacy
方法打印带有特殊前缀的字符串。但我们的新系统需要一个符合 ModernPrinter
接口的对象,该接口有一个 print_plain
方法来打印普通字符串。
Python
-
LegacyPrinter
是被适配者 (Adaptee),拥有一个名为print_legacy
的方法。 -
ModernPrinter
是目标接口 (Target),定义了客户端期望的print_plain
方法。 -
PrinterAdapter
是适配器 (Adapter)。它实现了ModernPrinter
接口,并在初始化时接收一个LegacyPrinter
的实例。它的print_plain
方法内部调用了_legacy_printer
的print_legacy
方法,完成了接口的转换。 -
client_code
函数是客户端。它接收一个ModernPrinter
类型的对象,并调用其print_plain
方法。它完全不知道底层实际执行的是LegacyPrinter
。 -
在使用时,我们创建了
LegacyPrinter
对象,然后用PrinterAdapter
将其包装起来,最后将适配器传递给client_code
。这样,不兼容的接口就被成功适配了。
from abc import ABC, abstractmethod
# --- Adaptee ---
# 遗留的打印机类,接口不兼容
class LegacyPrinter:
def print_legacy(self, text: str):
print(f"Legacy Printer: ### {text} ###")
# --- Target Interface ---
# 客户端期望的现代打印机接口
class ModernPrinter(ABC):
@abstractmethod
def print_plain(self, text: str):
pass
# --- Adapter (Object Adapter) ---
# 适配器类,实现 ModernPrinter 接口,并包含一个 LegacyPrinter 实例
class PrinterAdapter(ModernPrinter):
def __init__(self, legacy_printer: LegacyPrinter):
print("PrinterAdapter: Wrapping LegacyPrinter instance.")
self._legacy_printer = legacy_printer # 持有被适配者实例
def print_plain(self, text: str):
# 将对 print_plain 的调用转换为对 print_legacy 的调用
print("PrinterAdapter: Translating 'print_plain' call to 'print_legacy'.")
# 在这里可以做一些数据格式转换等操作,本例直接调用
self._legacy_printer.print_legacy(text)
# --- Client Code ---
# 客户端代码只与 ModernPrinter 接口交互
def client_code(printer: ModernPrinter, message: str):
print("\nClient: Requesting plain print...")
printer.print_plain(message)
# --- Usage ---
if __name__ == "__main__":
print("Creating a LegacyPrinter instance...")
legacy_printer = LegacyPrinter()
# legacy_printer.print_plain("Hello") # 这会报错,因为它没有 print_plain 方法
print("\nCreating a PrinterAdapter to wrap the LegacyPrinter...")
adapter = PrinterAdapter(legacy_printer)
# 现在适配器有了 print_plain 方法
print("\nUsing the adapter with the client code:")
client_code(adapter, "Hello, Adapter Pattern!")
print("\nUsing the legacy printer directly (for comparison):")
legacy_printer.print_legacy("Direct legacy call")
# 客户端代码现在可以通过适配器无缝使用遗留打印机
client_code(adapter, "Another message via adapter.")
Go
package adapter
import "fmt"
// --- Adaptee ---
// 遗留的打印机类,接口不兼容
type LegacyPrinter struct{}
func (lp *LegacyPrinter) PrintLegacy(text string) {
fmt.Printf("Legacy Printer: ### %s ###\n", text)
}
// --- Target Interface ---
// 客户端期望的现代打印机接口
type ModernPrinter interface {
PrintPlain(text string)
}
// --- Adapter (Object Adapter) ---
// 适配器类,实现 ModernPrinter 接口,并包含一个 LegacyPrinter 实例
type PrinterAdapter struct {
legacyPrinter *LegacyPrinter
}
func NewPrinterAdapter(legacyPrinter *LegacyPrinter) *PrinterAdapter {
fmt.Println("PrinterAdapter: Wrapping LegacyPrinter instance.")
return &PrinterAdapter{legacyPrinter: legacyPrinter}
}
func (pa *PrinterAdapter) PrintPlain(text string) {
fmt.Println("PrinterAdapter: Translating 'PrintPlain' call to 'PrintLegacy'.")
pa.legacyPrinter.PrintLegacy(text)
}
// --- Client Code ---
// 客户端代码只与 ModernPrinter 接口交互
func clientCode(printer ModernPrinter, message string) {
fmt.Println("\nClient: Requesting plain print...")
printer.PrintPlain(message)
}
测试
package adapter
import (
"fmt"
"testing"
)
func TestAdapter(t *testing.T) {
fmt.Println("Creating a LegacyPrinter instance...")
legacyPrinter := &LegacyPrinter{}
// legacyPrinter.PrintPlain("Hello") // 这会报错,因为它没有 PrintPlain 方法
fmt.Println("\nCreating a PrinterAdapter to wrap the LegacyPrinter...")
adapter := NewPrinterAdapter(legacyPrinter)
// 现在适配器有了 PrintPlain 方法
fmt.Println("\nUsing the adapter with the client code:")
clientCode(adapter, "Hello, Adapter Pattern!")
fmt.Println("\nUsing the legacy printer directly (for comparison):")
legacyPrinter.PrintLegacy("Direct legacy call")
// 客户端代码现在可以通过适配器无缝使用遗留打印机
clientCode(adapter, "Another message via adapter.")
}