策略模式下如何为策略类提供宿主类属性方法的访问接口
策略模式中让行为策略优雅访问宿主属性的实现
原代码的报错根因
- 你在
Duck的初始化逻辑里,直接把行为类的fly/quack方法赋值给自身实例属性,但没有完成上下文绑定。调用first_duck.fly()时,方法内部的self指向FlyWithWings实例,而非Duck实例,自然找不到挂在Duck上的wingspan属性。 - 逐次传参的方案之所以维护成本高,是因为把“行为需要读取宿主属性”的协作关系,变成了“构造策略时拷贝所有宿主属性值”的硬绑定。当策略需要读取10个以上属性时,每个策略类的构造函数都要重复声明一长串参数,一旦宿主的属性名调整,所有相关策略类都要同步修改。
核心实现思路
让策略类只持有对宿主对象的弱引用,而非逐次拷贝属性值。这种方式的耦合度最低:不管后续策略需要读取多少个宿主属性,都不需要修改构造参数列表,也不会因为漏传、错传参数出问题;同时弱引用可以避免Duck实例和策略实例的循环强引用导致的内存泄漏。
可运行的参考实现
import weakref # 飞行动作抽象基类,统一处理宿主绑定逻辑 class FlyBehavior: def bind_host(self, host_instance): # 用弱引用持有宿主,防止循环引用 self._host_ref = weakref.ref(host_instance) @property def host(self): host = self._host_ref() if host is None: raise ReferenceError("绑定的宿主实例已被销毁") return host class FlyWithWings(FlyBehavior): def fly(self): # 直接通过宿主引用读取属性,不需要额外传参 host = self.host print("I'm flying with wings") print(f"speed of {host.wingspan * 1.1}") class Quack: def quack(self): print("Quack, quack!") class Duck: def __init__(self, wingspan: int, quack_behavior, fly_behavior: FlyBehavior): self.wingspan = wingspan # 初始化时把自身绑定给需要访问上下文的飞行动作策略 fly_behavior.bind_host(self) # 将策略方法绑定到当前Duck实例 self.fly = fly_behavior.fly self.quack = quack_behavior.quack class MallardDuck(Duck): def __init__(self): super().__init__( wingspan=22, quack_behavior=Quack(), fly_behavior=FlyWithWings() ) if __name__ == "__main__": first_duck = MallardDuck() first_duck.quack() first_duck.fly()
方案的优势
- 维护成本不随属性数量上升:后续飞行动作需要读取宿主的体重、翼羽健康度等额外属性时,只需要在策略方法里通过
self.host.xxx读取即可,完全不需要修改Duck类的构造逻辑,也不用给所有策略类新增构造参数。 - 避免属性副本不一致的问题:如果逐次传参,策略里存的是初始化时的属性快照,当宿主的属性在运行时动态变化(比如鸭子换羽毛导致翼展改变),策略读到的会是旧值;而持有宿主引用的方案永远能读到实时的属性值。
- 复用性更强:策略类不依赖Duck的具体实现,只要其他类也提供
wingspan等约定的属性,就可以直接绑定同一个飞行动作策略使用。
常见误区:很多实现会把所有需要的参数直接塞到策略的构造函数里,比如
FlyWithWings(wingspan=22),这种写法本质是把策略和特定场景的静态值绑死,既无法响应运行时的属性变更,也会随着属性数量增加让参数列表越来越冗长。
内容的提问来源于stack exchange,提问作者Anders_K
相关产品推荐
相关产品推荐

