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) |
适配器和外观共同的实现套路:组合,不是继承 |
记忆口诀
接口对不上,适配器来翻译;步骤太啰嗦,外观包个入口。动机不同,套路相同——持有它,包一层。
动手改
- 给
PayAdapter补一个refund(yuan)退款方法,内部调用老 SDK 的do_refund(cents)(自己给LegacyPaySDK补上这个方法) - 把
App.start()改成支持参数skip_cache=True时跳过缓存预热 - 想一个你项目里"直接 new 三方 SDK"的地方,画一下它的适配器该长什么样
下一章:结构型模式:装饰器与代理 🐸