You need to enable JavaScript to run this app. 策略模式下如何为策略类提供宿主类属性方法的访问接口
优惠活动
大模型
产品
解决方案
定价
更多

策略模式下如何为策略类提供宿主类属性方法的访问接口

策略模式中让行为策略优雅访问宿主属性的实现

原代码的报错根因

  • 你在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.06 19:45:07