python--设计模式--20--外观模式vs策略模式
·
外观模式 vs 策略模式:完整对比与代码示例
外观模式(Facade)和策略模式(Strategy)都是非常经典的设计模式,但它们的设计初衷、要解决的问题以及代码结构完全不同。
一句话概括核心区别:外观模式是为了“简化复杂系统的使用”,而策略模式是为了“灵活切换算法实现”。
一、🎭 外观模式 (Facade) —— “一键启动电脑”
- 目的:为子系统中的一组复杂接口,提供一个统一的、简化的高级接口。
- 解决什么问题:复杂性。当一个子系统包含几十个类,调用流程极其繁琐时,外观模式提供一个“傻瓜式”入口,屏蔽内部细节。
- 本质:封装与聚合。把多个类的复杂调用,包装成一个简单的方法。
- 结构:创建一个新的类(Facade),该类内部持有子系统的多个对象,对外暴露几个简单的方法。
- 客户端感知:客户端只和 Facade 交互,不知道也不关心背后有多少个类在干活。
- 典型场景:复杂的第三方库集成、框架的入口类(如 Django 的
Model.objects.get背后封装了 SQL 查询构建、连接管理)。
二、🔄 策略模式 (Strategy) —— “出门选择交通工具”
- 目的:定义一系列可互换的算法(或业务规则),让客户端在运行时动态选择使用哪一种。
- 解决什么问题:多变的算法。当同一个业务有不同的实现方式(如不同的排序算法、不同的支付渠道),避免在代码里写大量的
if-else。 - 本质:委托与多态。将算法抽取成独立的类,让它们实现同一个接口,上下文(Context)只持有接口引用,运行时再注入具体实现。
- 结构:策略接口(Strategy) + 多个具体策略类(ConcreteStrategy) + 上下文类(Context,负责持有并调用策略)。
- 客户端感知:客户端必须知道有哪些具体策略,并主动选择/注入其中一个给 Context。
- 典型场景:电商系统根据会员等级计算折扣;支付方式选择(微信/支付宝/银行卡);地图导航的路径规划(步行/驾车/公交)。
三、📊 核心区别对比表
| 对比维度 | 外观模式 (Facade) | 策略模式 (Strategy) |
|---|---|---|
| 核心意图 | 简化复杂调用(统一入口) | 切换具体算法(行为独立) |
| 关注点 | 关注 “怎么把这些类拼起来完成一件事” | 关注 “同一件事的不同做法如何独立替换” |
| 设计阶段 | 通常用于架构层,应对系统臃肿 | 通常用于业务层,应对规则变化 |
| 类/对象关系 | 依赖关系:Facade 依赖于子系统 | 组合关系:Context 组合 Strategy 接口 |
| 运行时行为 | 静态的,客户端调用时 Facade 内部逻辑通常固定 | 动态的,客户端可以在运行时 setStrategy(new X()) 切换 |
| 是否隐藏细节 | 是。彻底隐藏底层类,客户端一无所知 | 否。客户端必须清楚不同策略的含义才能选择 |
| 典型代码形态 | 封装方法调用链 | 接口 + 多个实现类 + 运行时赋值 |
| 符合的设计原则 | 最少知识原则(只跟朋友说话) | 开闭原则(新增策略无需修改旧代码) |
四、💡 举个直观的生活例子
假设你要去 “下单购买一件商品”:
- 如果使用外观模式:你对着手机喊“下单”。外观模式背后的操作是:检查库存 → 生成订单 → 扣减库存 → 发起支付 → 通知物流。你只管喊,背后所有流程自动跑完。
- 如果使用策略模式:你对着手机说“用微信支付”或“用支付宝”。策略模式背后的操作是:无论选哪个,都执行
pay(amount)方法,但具体实现(调用微信SDK还是支付宝SDK)完全隔离。你必须指定用哪个策略,但不用管具体怎么调接口。
五、🧪 Python 代码示例
🎭 外观模式 —— 家庭影院“一键观影”
场景:你想看一场电影,但需要依次执行:开投影仪 → 设HDMI输入 → 开音响 → 调音量 → 关灯。这一套流程很繁琐。外观模式就是给你提供一个 “一键观影” 按钮。
# 1. 复杂的子系统 (Subsystems)
class Projector:
def on(self): print("🎬 投影仪已开启")
def off(self): print("🎬 投影仪已关闭")
def set_input(self, source): print(f"📡 信号源切换至: {source}")
class SoundSystem:
def on(self): print("🔊 音响已开启")
def off(self): print("🔊 音响已关闭")
def set_volume(self, level): print(f"🔊 音量调节至: {level}")
class Lights:
def dim(self, level): print(f"💡 灯光调暗至: {level}%")
def on(self): print("💡 灯光已全亮")
# 2. 外观类 (Facade) —— 提供统一入口
class HomeTheaterFacade:
def __init__(self, projector, sound, lights):
self.projector = projector
self.sound = sound
self.lights = lights
def watch_movie(self, movie):
print("\n=== 🎥 开始电影模式 ===")
self.lights.dim(20)
self.projector.on()
self.projector.set_input("HDMI 1")
self.sound.on()
self.sound.set_volume(50)
print(f"▶️ 正在播放: {movie}")
def end_movie(self):
print("\n=== 🎬 结束观影 ===")
self.sound.off()
self.projector.off()
self.lights.on()
# 3. 客户端调用 (Client)
if __name__ == "__main__":
facade = HomeTheaterFacade(Projector(), SoundSystem(), Lights())
facade.watch_movie("《阿凡达》")
facade.end_movie()
✅ 外观模式的效果:客户端面对的是
watch_movie这一个简单的方法。即使背后有几十个类,客户端也毫无感知,复杂性被彻底封装了。
🔄 策略模式 —— 商城的动态折扣计算
场景:双11购物车结算,折扣规则很多(新人、会员、满减)。客户端必须明确告诉系统“今天该用什么策略”,但无需关心这个策略内部是打8折还是减50。
from abc import ABC, abstractmethod
# 1. 策略接口 (Strategy Interface)
class DiscountStrategy(ABC):
@abstractmethod
def apply(self, total):
pass
# 2. 具体策略类 (Concrete Strategies)
class NoDiscount(DiscountStrategy):
def apply(self, total):
return total
class VipDiscount(DiscountStrategy):
def apply(self, total):
return total * 0.8 # 打8折
class BlackFridayDiscount(DiscountStrategy):
def apply(self, total):
return total * 0.5 if total > 200 else total * 0.9
# 3. 上下文类 (Context) —— 持有策略引用
class ShoppingCart:
def __init__(self, total, strategy: DiscountStrategy):
self.total = total
self._strategy = strategy
def set_strategy(self, strategy):
self._strategy = strategy
def checkout(self):
final_price = self._strategy.apply(self.total)
print(f"💰 原价: {self.total}, 实付: {final_price}")
return final_price
# 4. 客户端调用 (Client)
if __name__ == "__main__":
cart = ShoppingCart(300, NoDiscount())
cart.checkout() # 实付 300
cart.set_strategy(VipDiscount())
cart.checkout() # 实付 240
cart.set_strategy(BlackFridayDiscount())
cart.checkout() # 实付 150
✅ 策略模式的效果:客户端承担了 “选择” 的责任(明确指定用
VipDiscount还是BlackFridayDiscount),但把 “怎么算” 完全委托给了具体的策略类。算法之间相互独立,互不影响。
六、🎯 总结:如何做技术选型?
| 场景 | 推荐模式 |
|---|---|
| 觉得“这堆代码好乱,调用太麻烦”,想整理一下 | 外观模式 |
出现大量 if type == A then ... else if type == B...,且算法经常新增或修改 | 策略模式 |
| 整合第三方库或老旧遗留系统 | 外观模式 |
| 业务规则频繁变动,需要灵活扩展 | 策略模式 |
记忆口诀:
- 如果你写代码是为了 “少写几行调用” 👉 考虑 外观模式
- 如果你写代码是为了 “少改几行逻辑” 👉 考虑 策略模式
最终一句话:
外观模式让“使用”变得简单,策略模式让“扩展”变得简单。 两者可以共存,例如一个外观类内部,可能同时使用了好几种策略来完成一个复杂的业务流程。
更多推荐
所有评论(0)