跳到主要内容

适配器模式

简介

适配器模式(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_printerprint_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.")
}