作者: andylin02
学习章节: 第1章 欢迎来到设计模式世界:设计模式入门——策略模式
关键词:策略模式, Python, 设计原则, 封装变化, 多用组合少用继承, 鸭子类型, abc模块
策略模式Python深度解析:用鸭子类型优雅应对变化
大家好,我是 andylin02。在上一篇《Head First设计模式》Python版概览中,我们梳理了全书的核心脉络,知道设计模式是应对软件变化的利器,并快速预览了策略模式如何拯救Joe的鸭子模拟器。今天,我们将正式进入第1章,用Python的方式重新演绎这个经典故事,并深入理解它背后的设计原则。
一、开篇复习:我们为什么需要设计模式?
回顾上一篇的内容,我们强调了一个残酷的现实:软件中的需求总是在变。如果代码结构是硬编码的、铁板一块,一次微小改动就可能引发连锁崩溃。《Head First设计模式》则教你把变化的部分“关进笼子里”。而这一切的起点,就是第1章策略模式。它不只是一个具体模式,更是你理解“封装变化”、“多用组合、少用继承”、“面向接口编程”三大设计原则的最佳敲门砖。
今天,我们用Python的视角重走Joe的“鸭子困境”,你会惊讶地发现:Python的灵活性让设计模式更简洁,但背后的设计思想完全一致。
二、故事的开始:鸭子模拟器的灾难
Joe的游戏公司开发了一款模拟池塘的游戏,里面有各种鸭子:绿头鸭、红头鸭、橡皮鸭、诱饵鸭……它们都会游泳、会叫,但外观各不相同。Joe用经典的继承设计了 Duck 基类:
class Duck:
def quack(self):
print("Quack! Quack!")
def swim(self):
print("All ducks can swim.")
def display(self):
raise NotImplementedError
一切完美,直到产品经理说:“让鸭子会飞。”
Joe毫不犹豫地在 Duck 里加了 fly() 方法。结果,橡皮鸭、诱饵鸭也继承了飞行能力,橡皮鸭在屏幕上扑腾着飞了起来——这显然不对。Joe赶紧在 RubberDuck 里覆盖 fly() 为空,又修改 quack() 为“吱吱叫”……很快,行为代码在子类间疯狂重复,每次新增鸭子类型都胆战心惊。
问题根因:继承将行为静态绑定在类上,无法灵活应对变化。
三、接口?也不是银弹
Joe想到用接口(Python里可以用抽象基类)来定义 Flyable 和 Quackable,让会飞的鸭子实现它们:
from abc import ABC, abstractmethod
class Flyable(ABC):
@abstractmethod
def fly(self):
pass
class Quackable(ABC):
@abstractmethod
def quack(self):
pass
但这导致新问题:所有会飞的鸭子都要实现完全相同的 fly() 方法,代码重复。接口抽象了能力,却没有提供代码复用的机制。
四、设计原则拯救世界
面对两难处境,书中给出了第一个设计原则:
找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
在鸭子模拟器中,“飞行行为”和“叫声行为”正是变化的部分,应该抽离成独立的类族。
接着第二个原则:
针对接口编程,而不是针对实现。
这里的“接口”是广义的,在Python中可以是抽象基类,也可以直接依赖“鸭子类型”——只要对象有 fly() 方法,就能当飞行行为用。不过为了结构清晰,我们仍用抽象基类来明确定义契约。
第三个原则:
多用组合,少用继承。
每个鸭子类不再通过继承获得飞行能力,而是组合一个飞行行为对象和一个叫声行为对象。这样可以在运行时动态切换行为,复用代码也更灵活。
五、策略模式正式登场
这三个原则汇聚起来,就形成了策略模式。
定义:策略模式定义了算法族,分别封装起来,让它们之间可以互相替换。此模式让算法的变化独立于使用算法的客户。
在鸭子例子中,“算法族”就是飞行行为和叫声行为。我们将其封装成独立的类,可随时替换,鸭子(客户)并不关心内部实现。
UML类图
#mermaid-svg-aZ9h8Q9gEZlbb4Is{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-aZ9h8Q9gEZlbb4Is .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .error-icon{fill:#552222;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .marker{fill:#333333;stroke:#333333;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .marker.cross{stroke:#333333;}#mermaid-svg-aZ9h8Q9gEZlbb4Is svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-aZ9h8Q9gEZlbb4Is p{margin:0;}#mermaid-svg-aZ9h8Q9gEZlbb4Is g.classGroup text{fill:#9370DB;stroke:none;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-aZ9h8Q9gEZlbb4Is g.classGroup text .title{font-weight:bolder;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .cluster-label text{fill:#333;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .cluster-label span{color:#333;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .cluster-label span p{background-color:transparent;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .cluster text{fill:#333;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .cluster span{color:#333;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .nodeLabel,#mermaid-svg-aZ9h8Q9gEZlbb4Is .edgeLabel{color:#131300;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .label text{fill:#131300;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .labelBkg{background:#ECECFF;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .classTitle{font-weight:bolder;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .node rect,#mermaid-svg-aZ9h8Q9gEZlbb4Is .node circle,#mermaid-svg-aZ9h8Q9gEZlbb4Is .node ellipse,#mermaid-svg-aZ9h8Q9gEZlbb4Is .node polygon,#mermaid-svg-aZ9h8Q9gEZlbb4Is .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-aZ9h8Q9gEZlbb4Is g.clickable{cursor:pointer;}#mermaid-svg-aZ9h8Q9gEZlbb4Is g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-aZ9h8Q9gEZlbb4Is g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .dashed-line{stroke-dasharray:3;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-aZ9h8Q9gEZlbb4Is #compositionStart,#mermaid-svg-aZ9h8Q9gEZlbb4Is .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-aZ9h8Q9gEZlbb4Is #compositionEnd,#mermaid-svg-aZ9h8Q9gEZlbb4Is .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-aZ9h8Q9gEZlbb4Is #dependencyStart,#mermaid-svg-aZ9h8Q9gEZlbb4Is .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-aZ9h8Q9gEZlbb4Is #dependencyStart,#mermaid-svg-aZ9h8Q9gEZlbb4Is .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-aZ9h8Q9gEZlbb4Is #extensionStart,#mermaid-svg-aZ9h8Q9gEZlbb4Is .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-aZ9h8Q9gEZlbb4Is #extensionEnd,#mermaid-svg-aZ9h8Q9gEZlbb4Is .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-aZ9h8Q9gEZlbb4Is #aggregationStart,#mermaid-svg-aZ9h8Q9gEZlbb4Is .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-aZ9h8Q9gEZlbb4Is #aggregationEnd,#mermaid-svg-aZ9h8Q9gEZlbb4Is .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-aZ9h8Q9gEZlbb4Is #lollipopStart,#mermaid-svg-aZ9h8Q9gEZlbb4Is .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-aZ9h8Q9gEZlbb4Is #lollipopEnd,#mermaid-svg-aZ9h8Q9gEZlbb4Is .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-aZ9h8Q9gEZlbb4Is .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-aZ9h8Q9gEZlbb4Is :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
has a
has a
«abstract»
Duck
+fly_behavior : FlyBehavior
+quack_behavior : QuackBehavior
+perform_fly() : void
+perform_quack() : void
+swim() : void
+display() : void
+set_fly_behavior(FlyBehavior) : void
+set_quack_behavior(QuackBehavior) : void
MallardDuck
+display() : void
RedheadDuck
+display() : void
RubberDuck
+display() : void
«abstract»
FlyBehavior
+fly() : void
FlyWithWings
+fly() : void
FlyNoWay
+fly() : void
FlyRocketPowered
+fly() : void
«abstract»
QuackBehavior
+quack() : void
Quack
+quack() : void
Squeak
+quack() : void
MuteQuack
+quack() : void
六、Python完整源码
我们用Python的 abc 模块定义抽象行为,借助组合实现策略模式。
from abc import ABC, abstractmethod
# ========== 飞行行为抽象基类及实现 ==========
class FlyBehavior(ABC):
@abstractmethod
def fly(self):
pass
class FlyWithWings(FlyBehavior):
def fly(self):
print("I'm flying with wings!")
class FlyNoWay(FlyBehavior):
def fly(self):
print("I can't fly.")
class FlyRocketPowered(FlyBehavior):
def fly(self):
print("I'm flying with a rocket!")
# ========== 叫声行为抽象基类及实现 ==========
class QuackBehavior(ABC):
@abstractmethod
def quack(self):
pass
class Quack(QuackBehavior):
def quack(self):
print("Quack! Quack!")
class Squeak(QuackBehavior):
def quack(self):
print("Squeak! Squeak!")
class MuteQuack(QuackBehavior):
def quack(self):
print("<< Silence >>")
# ========== 鸭子抽象基类 ==========
class Duck(ABC):
def __init__(self):
# 行为对象由子类负责初始化
self.fly_behavior = None
self.quack_behavior = None
def perform_fly(self):
self.fly_behavior.fly() # 委托给行为对象
def perform_quack(self):
self.quack_behavior.quack()
@staticmethod
def swim():
print("All ducks float, even decoys!")
@abstractmethod
def display(self):
pass
# 动态设置行为,支持运行时切换
def set_fly_behavior(self, fb: FlyBehavior):
self.fly_behavior = fb
def set_quack_behavior(self, qb: QuackBehavior):
self.quack_behavior = qb
# ========== 具体鸭子子类 ==========
class MallardDuck(Duck):
def __init__(self):
super().__init__()
self.fly_behavior = FlyWithWings()
self.quack_behavior = Quack()
def display(self):
print("I'm a real Mallard duck")
class RedheadDuck(Duck):
def __init__(self):
super().__init__()
self.fly_behavior = FlyWithWings()
self.quack_behavior = Quack()
def display(self):
print("I'm a redhead duck")
class RubberDuck(Duck):
def __init__(self):
super().__init__()
self.fly_behavior = FlyNoWay()
self.quack_behavior = Squeak()
def display(self):
print("I'm a rubber duckie")
class DecoyDuck(Duck):
def __init__(self):
super().__init__()
self.fly_behavior = FlyNoWay()
self.quack_behavior = MuteQuack()
def display(self):
print("I'm a decoy duck")
# ========== 测试 ==========
if __name__ == "__main__":
mallard = MallardDuck()
mallard.display()
mallard.perform_quack()
mallard.perform_fly()
print("—————–")
rubber = RubberDuck()
rubber.display()
rubber.perform_quack()
rubber.perform_fly()
# 动态改变行为——策略模式的真正威力
print("\\n>>> 给橡皮鸭安装火箭引擎!")
rubber.set_fly_behavior(FlyRocketPowered())
rubber.perform_fly()
运行结果:
I'm a real Mallard duck
Quack! Quack!
I'm flying with wings!
—————–
I'm a rubber duckie
Squeak! Squeak!
I can't fly.
>>> 给橡皮鸭安装火箭引擎!
I'm flying with a rocket!
橡皮鸭原本不会飞,通过 set_fly_behavior 动态装备了火箭飞行能力。这正是策略模式运行时扩展的魅力。
七、深度思考:策略模式如何体现设计原则?
| 封装变化 | 将飞行、叫声行为抽离为独立类族,与不变的 swim()、display() 分离。 |
| 面向接口编程 | Duck 依赖 FlyBehavior 和 QuackBehavior 抽象,不依赖具体实现。 |
| 多用组合,少用继承 | 每个鸭子对象组合了行为组件,而非硬编码继承。 |
| 开闭原则(后续章节) | 新增飞行方式(如火箭飞行)只需新增一个类,无需修改原有代码。 |
这种设计让系统弹性大增,你能在不触碰现有代码的前提下扩展新行为,完美应对需求变化。
八、Python特色:更轻量级的策略——函数就是策略
Python的函数是一等公民,这让我们可以直接用函数作为策略,省去创建类的麻烦。比如:
from typing import Callable
class Duck:
def __init__(self, fly_fn: Callable, quack_fn: Callable):
self._fly = fly_fn
self._quack = quack_fn
def perform_fly(self):
self._fly()
def perform_quack(self):
self._quack()
# 策略函数
def fly_with_wings():
print("Flying with wings!")
def fly_no_way():
print("Can't fly.")
def quack_loud():
print("QUACK!")
# 使用
mallard = Duck(fly_with_wings, quack_loud)
mallard.perform_fly()
这种“函数式策略”在轻量场景下非常Pythonic,但缺点是失去了类型契约的显式约束。如果策略需要复杂状态或多种方法,类依然是更好的选择。
九、策略模式 vs 其他相似设计模式
学习策略模式时,它很容易与状态模式和命令模式混淆。我们做一个前瞻性对比:
1. 策略模式 vs 状态模式
- 相同点:都通过组合不同行为对象,并在运行时切换它们来改变行为。
- 不同点:
- 意图:策略是让客户端主动选择不同的算法;状态是对象内部状态改变时自动切换行为。
- 客户端感知:策略模式中,客户端明确知道并选择策略;状态模式中,状态切换对客户端透明。
- 类比:策略像导航软件的“最快/最短/躲避拥堵”路径偏好;状态像自动洗衣机的“进水→洗涤→漂洗→脱水”程序。
2. 策略模式 vs 命令模式
- 相同点:都可能把“动作”封装成对象。
- 不同点:
- 命令模式关注请求的封装,解耦调用者与执行者,常支持撤销、队列;策略模式关注算法的互换,描述做一件事的不同方式。
- 命令通常是一次性或需要被记录的操作;策略则一般是持久附加在对象上的行为配置。
- 类比:命令像遥控器上的“音量+”按钮;策略像汽车可选的“经济/运动”驾驶模式。
这些对比能帮助你在后续章节抓住模式的本质区别。
十、本章读书笔记与总结
核心知识点清单
- 继承虽然提供复用,但容易将行为静态绑定,需求变化时导致代码脆弱。
- 单纯使用接口能抽离行为,但无法复用实现,产生大量重复代码。
- 设计原则1:找出变化的部分并封装之。
- 设计原则2:面向接口编程,而非面向实现。
- 设计原则3:多用组合,少用继承。
- 策略模式定义:定义一系列算法,封装每一个算法,并使它们可以相互替换。策略模式让算法的变化独立于使用它的客户。
- 结构:抽象策略接口 → 具体策略实现 → 上下文持有策略引用,通过委托执行行为。
- Python中可用 abc 抽象基类实现,或更轻量地直接使用函数。
- 可在运行时动态切换策略,提供极高的灵活度。
- 适用场景:多个类只在行为上有区别;需要不同算法变体;算法使用了客户端不应知道的复杂数据;类中定义了多种行为,以大量条件语句实现。
一张图牢记策略模式
#mermaid-svg-lz6lojsIwSzHvNiN{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-lz6lojsIwSzHvNiN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lz6lojsIwSzHvNiN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lz6lojsIwSzHvNiN .error-icon{fill:#552222;}#mermaid-svg-lz6lojsIwSzHvNiN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lz6lojsIwSzHvNiN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lz6lojsIwSzHvNiN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lz6lojsIwSzHvNiN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lz6lojsIwSzHvNiN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lz6lojsIwSzHvNiN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lz6lojsIwSzHvNiN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lz6lojsIwSzHvNiN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lz6lojsIwSzHvNiN .marker.cross{stroke:#333333;}#mermaid-svg-lz6lojsIwSzHvNiN svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lz6lojsIwSzHvNiN p{margin:0;}#mermaid-svg-lz6lojsIwSzHvNiN .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-lz6lojsIwSzHvNiN .cluster-label text{fill:#333;}#mermaid-svg-lz6lojsIwSzHvNiN .cluster-label span{color:#333;}#mermaid-svg-lz6lojsIwSzHvNiN .cluster-label span p{background-color:transparent;}#mermaid-svg-lz6lojsIwSzHvNiN .label text,#mermaid-svg-lz6lojsIwSzHvNiN span{fill:#333;color:#333;}#mermaid-svg-lz6lojsIwSzHvNiN .node rect,#mermaid-svg-lz6lojsIwSzHvNiN .node circle,#mermaid-svg-lz6lojsIwSzHvNiN .node ellipse,#mermaid-svg-lz6lojsIwSzHvNiN .node polygon,#mermaid-svg-lz6lojsIwSzHvNiN .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lz6lojsIwSzHvNiN .rough-node .label text,#mermaid-svg-lz6lojsIwSzHvNiN .node .label text,#mermaid-svg-lz6lojsIwSzHvNiN .image-shape .label,#mermaid-svg-lz6lojsIwSzHvNiN .icon-shape .label{text-anchor:middle;}#mermaid-svg-lz6lojsIwSzHvNiN .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-lz6lojsIwSzHvNiN .rough-node .label,#mermaid-svg-lz6lojsIwSzHvNiN .node .label,#mermaid-svg-lz6lojsIwSzHvNiN .image-shape .label,#mermaid-svg-lz6lojsIwSzHvNiN .icon-shape .label{text-align:center;}#mermaid-svg-lz6lojsIwSzHvNiN .node.clickable{cursor:pointer;}#mermaid-svg-lz6lojsIwSzHvNiN .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-lz6lojsIwSzHvNiN .arrowheadPath{fill:#333333;}#mermaid-svg-lz6lojsIwSzHvNiN .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lz6lojsIwSzHvNiN .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lz6lojsIwSzHvNiN .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lz6lojsIwSzHvNiN .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-lz6lojsIwSzHvNiN .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lz6lojsIwSzHvNiN .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-lz6lojsIwSzHvNiN .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lz6lojsIwSzHvNiN .cluster text{fill:#333;}#mermaid-svg-lz6lojsIwSzHvNiN .cluster span{color:#333;}#mermaid-svg-lz6lojsIwSzHvNiN div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-lz6lojsIwSzHvNiN .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-lz6lojsIwSzHvNiN rect.text{fill:none;stroke-width:0;}#mermaid-svg-lz6lojsIwSzHvNiN .icon-shape,#mermaid-svg-lz6lojsIwSzHvNiN .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lz6lojsIwSzHvNiN .icon-shape p,#mermaid-svg-lz6lojsIwSzHvNiN .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-lz6lojsIwSzHvNiN .icon-shape .label rect,#mermaid-svg-lz6lojsIwSzHvNiN .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lz6lojsIwSzHvNiN .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-lz6lojsIwSzHvNiN .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-lz6lojsIwSzHvNiN :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
委托
客户端
上下文 Duck
策略接口 FlyBehavior
具体策略 FlyWithWings
具体策略 FlyNoWay
具体策略 FlyRocketPowered
客户端只需指定策略,上下文将实际工作委托给策略对象。
十一、下篇预告:第2章 · 观察者模式(Python版)
今天我们通过鸭子的故事,用Python实现了策略模式,并深刻理解了“封装变化”、“面向接口编程”、“多用组合”三大原则。下一章,我们将进入观察者模式:想象你订阅了一份报纸,一旦有新刊出版,报社就会自动送上门来——这就是观察者模式的核心。我们将用Python构建一个气象站数据推送系统,并对比推模型和拉模型,还会看看如何用Python的 Observable 简化实现。敬请期待!
本文为个人学习笔记,仅用于知识分享。如有错误,欢迎指正。
👍🏻 点赞 + 收藏 + 分享,让更多开发者看到这篇深度解析!❤️ 如果觉得有用,请给个赞支持一下作者!

