这是一个有效的策略模式吗?



我需要使用外部方法来增强类的行为,因此我利用了策略模式。

首先,我为方法的签名定义了一个接口:

class ILabel(ABC):
@abstractmethod
def get_label(self, obj):
pass

和该接口的实现:

class Label(ILabel):
def __init__(self, prefix):
self.prefix = prefix
def get_label(self, merchandise, obj):
return self.prefix + str(obj) + merchandise.name

以及我想要扩展其算法的类:

class Merchandise:
def __init__(self):
self.name = "__a_name"
def get_label(self, obj):
return str(obj) + self.name
def display(self, obj, get_label=None):
if get_label:
self.get_label = types.MethodType(get_label, self)
print(self.get_label(obj))

最后是调用者:

# default behavior
x = Merchandise().display("an_obj")
# augmented behavior
label = Label("a_prefix__")
y = Merchandise().display("an_obj", label)
print(f"Default output: {x}")
print(f"Augmented output: {y}")

输出应该是:

Default output: an_obj__a_name
Augmented output: a_prefix__an_obj__a_name

两个问题:

  • Given而不是"orphan"方法(因为没有更好的词),我在类中发送一个引用self的方法;这是否仍然被认为是策略模式,还是一种更接近这种设计的不同模式?

  • 由于我在注册方法(即types.MethodType(get_label, self))时传递了对Merchandise的引用,因此Label类中的get_label方法引用了一个实例Merchandise。例如:

    def get_label(self, merchandise, obj):
    

    问题是,merchandise引用是否有更好的命名约定?

更新在努力提供一个最小的工作示例时,大量的上下文被分割,这可能会导致认为get_label方法可以是无状态的(即,没有对Merchandise实例的引用)。Label.get_label已更新以澄清这一点。

给定而不是"孤儿";方法(因为没有更好的词),我在类中发送一个引用self的方法;这是否仍然被认为是策略模式,还是一种更接近这种设计的不同模式?

我仍然认为这是策略,但请继续阅读。

由于我在注册方法(即类型)时传递了对商品的引用。方法类型(get_label, self)),在Label类中get_label的正确定义是:

这就是为什么你应该采取不同的方法。

你实现策略默认行为的逻辑是反向的。"为obj获取标签的策略"是没有理由的。应该是Merchandise类的方法,除非您碰巧在那里存储了默认实现。即使它不需要是一个普通的方法,因为它不需要self做任何

这意味着代码太复杂(因为您不必要地使用types.MethodType机制并动态修补类),并且还具有意外的状态行为:当您使用get_label的非None值调用display时,该策略将影响未来display的调用,其中传递None

如果你不想要有状态的行为,那么你需要另一种方式的默认设置逻辑——设置一个本地而不是修改类:

class Merchandise:
@staticmethod
def get_label(obj):
return str(obj)
def display(self, obj, get_label=None):
if get_label is None:
get_label = Merchandise.get_label
print(get_label(obj))

虽然我们实际上不需要"用默认值替换None"模式,因为我们不打算改变参数:

class Merchandise:
@staticmethod
def get_label(obj):
return str(obj)
def display(self, obj, get_label=Merchandise.get_label):
print(get_label(obj))

这个小例子可以更简单:

class Merchandise:    
def display(self, obj, get_label=str):
print(get_label(obj))
# although *this* doesn't rely on `self`, either....

如果你确实想要有状态的行为,那么你应该在初始化时设置状态,或者稍后显式地设置,或者两者都设置:

class Merchandise:
def __init__(self, get_label=str):
self.get_label = get_label
@property
def get_label(self): return self._get_label
@get_label.setter
def get_label(self, value):
# may as well do a little verification
if not callable(value):
raise TypeError("get_label strategy must be callable")
self._get_label = value
def display(self, obj):
print(self.get_label(obj))
注意这里的self.get_label(obj)而不是一个方法调用;Python将发现get_label作为实例的属性,在尝试在类中查找它之前;找到一个可调用对象后,它就调用该对象。

最新更新