【python 设计模式】适配器模式

2024-01-10 00:00:00

适配器模式充当两个不兼容接口之间的桥梁,使得原本因接口不匹配而无法协同工作的类能够一起工作。

工作原理

适配器模式主要涉及三个核心角色:

  1. 目标接口(Target):这是客户端期望并使用的接口。它定义了客户端需要调用的方法,是系统业务逻辑所期待的规范;

  2. 被适配者(Adaptee):这是已经存在的、功能完备但接口与目标接口不兼容的类或组件。它拥有我们需要的功能,但其方法命名或调用方式不符合当前系统的要求;

  3. 适配器(Adapter):这是模式的关键,它实现了目标接口,并在内部持有一个被适配者的实例。适配器的职责是将客户端通过目标接口发起的请求,“翻译”并转发给被适配者的相应方法,最终将结果以目标接口期望的格式返回给客户端

对象适配器模式

对象适配器采用组合的方式,适配器类持有被适配者的一个对象实例。

假设系统原有 AliPay 和 WechatPay 类,但它们的支付方法名不同。现在需要统一调用接口:

# 被适配者:已有的、接口不统一的类
class AliPay:
    def alipay_api(self, amount):
        print(f"支付宝支付{amount}元")


class WechatPay:
    def wechat_pay_api(self, amount):
        print(f"微信支付{amount}元")


# 目标接口:期望的统一支付接口
class TargetPayment:
    def pay(self, amount):
        pass


# 对象适配器
class PaymentAdapter(TargetPayment):
    def __init__(self, payment_object, payment_method_name):
        self.payment_object = payment_object  # 组合:持有被适配者对象
        self.payment_method = payment_method_name
        self._check_method()

    def _check_method(self):
        """检查被适配对象是否有所需方法"""
        if not hasattr(self.payment_object, self.payment_method):
            raise AttributeError(f"支付方式不支持: {self.payment_method}")

    def pay(self, amount):
        # 关键步骤:将目标接口调用适配到具体方法
        pay_func = getattr(self.payment_object, self.payment_method)
        return pay_func(amount)


# 客户端代码
if __name__ == "__main__":
    # 创建被适配者
    ali = AliPay()
    wechat = WechatPay()

    # 创建适配器,统一接口
    ali_adapter = PaymentAdapter(ali, "alipay_api")
    wechat_adapter = PaymentAdapter(wechat, "wechat_pay_api")

    # 客户端通过统一的Target接口调用
    ali_adapter.pay(100)
    wechat_adapter.pay(200)

在这个例子中,PaymentAdapter 作为适配器,通过组合持有了 AliPay 或 WechatPay 的实例,并将统一的 pay() 方法调用适配到各自的 alipay_api() 或 wechat_pay_api() 方法上。

类适配器模式

类适配器通过多继承来实现,适配器类同时继承目标接口(或类)和被适配者。这种方式在 Python 中可行,但增加了类之间的耦合度。

假设有两个公司的员工类,方法相似但接口命名不同。

from abc import ABCMeta, abstractmethod

# 目标接口(A公司协议)
class ACpnStaff(metaclass=ABCMeta):
    @abstractmethod
    def set_name(self, name):
        pass
    @abstractmethod
    def set_phone(self, phone):
        pass


# 被适配者(B公司协议)
class BCpnStaff:
    def __init__(self, id):
        self.id = id
        self.name = ""
        self.telephone = ""

    def set_name(self, name):  # 注意:这里方法名巧合相同,但通常不同
        print(f"B协议 setName ..id:{self.id}")
        self.name = name

    def set_phone(self, phone):
        print(f"B协议 setPhone ..id:{self.id}")
        self.telephone = phone


# 类适配器 (通过多继承)
class CpnStaffAdapter(ACpnStaff, BCpnStaff):
    def __init__(self, id):
        # 调用BCpnStaff的初始化
        BCpnStaff.__init__(self, id)

    # 由于方法名相同,这里可能不需要重写。
    # 如果方法名不同,则需要在这里重写目标接口方法,并调用父类(被适配者)的方法。
    # 例如:def set_name(self, name): return super().setName(name)

# 客户端
if __name__ == "__main__":
    adapter = CpnStaffAdapter("123")
    adapter.set_name("张三")  # 实际调用的是BCpnStaff.set_name
    adapter.set_phone("13800138000")

返回首页

本文总阅读量  次
皖ICP备17026209号-3
总访问量: 
总访客量: