🐸 青蛙编程网
首页 / Python / 设计模式 / 4. 结构型模式:适配器与外观

4. 结构型模式:适配器与外观

这是什么:适配器 = 把旧接口包装成项目需要的新接口,两边都不用改;外观 = 给一堆复杂子系统套一个简单的统一入口。两者都是结构型:管代码怎么对接、怎么组装。

为什么用:三方 SDK 的方法名、参数单位跟项目对不上,直接改它的代码不可能,硬在业务代码里到处换算又难维护;同理,启动一个应用要依次连数据库、预热缓存、注册路由,每个调用方都写一遍这三步,漏一步就出事。

如何用:先给老支付 SDK 写适配器,再把应用启动的三步收进一个外观方法。

适配器:老接口包成新接口

三方 SDK 按"分"计费、方法叫 do_pay,项目统一按"元"调 pay——写一层包装做翻译:

class LegacyPaySDK:
    """三方老 SDK:方法名和参数单位都改不了"""

    def do_pay(self, cents):
        print(f"老 SDK 扣款 {cents} 分")


class PayAdapter:
    """适配器:把老 SDK 包成项目统一的支付接口"""

    def __init__(self, sdk):
        self.sdk = sdk

    def pay(self, yuan):                  # 项目里统一用"元"
        self.sdk.do_pay(round(yuan * 100))   # 翻译成"分",转交老 SDK


PayAdapter(LegacyPaySDK()).pay(9.9)

你会看到:

老 SDK 扣款 990 分

业务代码只认识 pay(9.9),完全不知道"分"的存在。哪天换另一家 SDK,重写适配器即可,全项目调用处零改动。(顺带留意 round(yuan * 100):浮点数 9.9 * 100 其实是 990.0000000000001,金额换算记得取整。)

外观:三步启动包成一个入口

没有外观时,每个要启动应用的地方都得写满三步;写一个外观类,把步骤藏起来:

class Database:
    def connect(self):
        print("数据库已连接")


class Cache:
    def warm(self):
        print("缓存已预热")


class Router:
    def register(self):
        print("路由已注册")


class App:
    """外观:复杂启动流程 → 一个 start()"""

    def __init__(self):
        self.db = Database()
        self.cache = Cache()
        self.router = Router()

    def start(self):
        self.db.connect()
        self.cache.warm()
        self.router.register()
        print("应用启动完成")


App().start()

你会看到:

数据库已连接
缓存已预热
路由已注册
应用启动完成

调用方一句 App().start(),三步的顺序、依赖全被外观守住,想漏都漏不了。

适配器 vs 外观:都是包装,动机不同

适配器 外观
包谁 一个对不上接口的旧对象 一堆繁琐的子系统
目的 接口翻译:旧的接上新的用 简化入口:十步变一步
例子 老 SDK → 统一支付接口 三步启动 → start()

本章解读

代码 作用
PayAdapter(sdk).pay(9.9) 适配器持有旧对象,新方法内部做翻译
round(yuan * 100) 单位换算 + 取整,浮点金额必做
App().start() 外观:一个入口带起整条流程
包装层持有被包对象(self.sdk) 适配器和外观共同的实现套路:组合,不是继承

记忆口诀

接口对不上,适配器来翻译;步骤太啰嗦,外观包个入口。动机不同,套路相同——持有它,包一层。

动手改

  1. 给 PayAdapter 补一个 refund(yuan) 退款方法,内部调用老 SDK 的 do_refund(cents)(自己给 LegacyPaySDK 补上这个方法)
  2. 把 App.start() 改成支持参数 skip_cache=True 时跳过缓存预热
  3. 想一个你项目里"直接 new 三方 SDK"的地方,画一下它的适配器该长什么样

下一章:结构型模式:装饰器与代理 🐸

📥 下载本章代码

ZIP 内含本章全部代码文件与说明,解压即用,与教程内容同步生成。