我最近读了《 Head First设计模式》一书,以努力成为一个更高效,更好的Python程序员。不幸的是,本书中的代码示例使用Java。

我不是第一个希望本书有Python版本的人,但是我认为编写Python代码将是一个很棒的练习。

我ve尝试在下面的Python“ Head First设计模式”第1章中实现策略设计模式。现在,我知道Python在一般意义上不是Java,但从本质上来说并不是。因此,我敢肯定我可以对这段代码做更多的Python事情。

也许我们可以使用内置的Python Duck实现set_fly_behavior()set_quack_behavior()property

还有其他建议吗?

#!/usr/bin/env python

################################################################################
# Abstract Duck class and concrete Duck type classes.
################################################################################
class Duck(object):
    def __init__(self):
        pass

    def set_fly_behavior(self, fb):
        self.fly_behavior = fb

    def set_quack_behavior(self, qb):
        self.quack_behavior = qb

    def perform_fly(self):
        self.fly_behavior.fly()

    def perform_quack(self):
        self.quack_behavior.quack()

    def swim(self):
        print "All ducks float, even decoys!"

class MallardDuck(Duck):
    def __init__(self):
        Duck.__init__(self)
        fly_instance = Fly()
        self.set_fly_behavior(fly_instance)
        quack_instance = Quack()
        self.set_quack_behavior(quack_instance)
        print "I'm a real Mallard Duck"

class ModelDuck(Duck):
    def __init__(self):
        Duck.__init__(self)
        fly_no_way_instance = FlyNoWay()
        self.set_fly_behavior(fly_no_way_instance)
        squeak_instance = SqueakQuack()
        self.set_quack_behavior(squeak_instance)
        print "I'm a Model Duck"

class DecoyDuck(Duck):
    def __init__(self):
        Duck.__init__(self)
        fly_no_way_instance = FlyNoWay()
        self.set_fly_behavior(fly_no_way_instance)
        mute_instance = MuteQuack()
        self.set_quack_behavior(mute_instance)
        print "I'm a Decoy Duck"

################################################################################
# Fly behavior interface and behavior implementation classes.
################################################################################
class FlyBehavior():
    def __init__(self):
        pass

class Fly(FlyBehavior):
    def fly(self):
        print "I'm flying!!"

class FlyNoWay(FlyBehavior):
    def fly(self):
        print "I can't fly"

class FlyRocketPowered(FlyBehavior):
    def fly(self):
        print "I'm flying with a rocket!"

################################################################################
# Quack behavior interface and behavior implementation classes.
################################################################################
class QuackBehavior():
    def __init__(self):
        pass

class Quack(QuackBehavior):
    def quack(self):
        print "Quack"

class MuteQuack(QuackBehavior):
    def quack(self):
        print "<< Silence >>"

class SqueakQuack(QuackBehavior):
    def quack(self):
        print "Squeak"

################################################################################
# Test Code.
################################################################################
if __name__ == "__main__":

    print '#'*80
    print "Mallard Duck"
    print '#'*80
    mallard = MallardDuck()
    mallard.perform_quack()
    mallard.perform_fly()

    print '#'*80
    print "Model Duck"
    print '#'*80
    model = ModelDuck()
    model.perform_fly()
    fly_rocket_powered_instance = FlyRocketPowered()
    model.set_fly_behavior(fly_rocket_powered_instance)
    model.perform_fly()

    print '#'*80
    print "Decoy Duck"
    print '#'*80
    decoy = DecoyDuck()
    decoy.perform_fly()
    decoy.perform_quack()


#1 楼

_details中的魔鬼


您的建议不错,但您不需要财产;您可以只使用普通属性。 setter所做的全部工作就是设置变量,因此只需进行设置即可。所以,这个:

self.set_fly_behavior(fly_instance)


可以变成这个:

self._fly_behaviour = fly_instance


注意我对self._fly_behaviour的使用。由于它是该类的实现细节,因此最佳实践是在变量名称前添加下划线。这可以告诉程序员,它不是设计用于外部的。

此外:命名行为

名称perform_quack()perform_fly()完全不是Pythonic的。为什么不只是quack()fly()?表演并没有告诉我们任何东西,除了可能将实现细节泄漏到外部(这些委托)。调用者不需要关心这件事。


ABCs

除此之外,您还可以创建基类Abstract Base Classs:

from abc import ABCMeta, abstractmethod

...

class QuackBehavior(object):
    __metaclass__ = ABCMeta

    @abstractmethod
    def quack(self):
        pass

    @classmethod
    def __subclasshook__(cls, Check):
        required = ["quack"]
        rtn = True
        for r in required:
          if not any(r in vars(BaseClass) for BaseClass in Check.__mro__):
            rtn = NotImplemented
        return rtn


这使您可以为类提供更多的Pythonic接口。

(还请注意,删除了空的构造函数。Python还是提供了一个空的构造函数,因此,如果您不对其进行任何操作,请不要麻烦定义它。这只是无用的额外代码。)


一种功能性方法

这是一个非常强大的Python解决方案。如果您知道这些行为始终只是一个函数,那么我会认为不需要类。

相反,请记住Python函数是一流的对象:

def quack(self):
    print("Quack")

def mute_quack(self):
    print "<< Silence >>"

some_duck = Duck()
some_duck.quack = mute_quack



混合方法

我们可以做一个小的修改:通过使用属性将函数转换为方法,以便可以正常调用它们。

(我假设您需要访问方法中的self;如果不需要,则忽略该参数,它将像静态方法一样按原样工作。)

import types

class Duck(object):
    ...

    @property
    def fly(self):
        return self._fly

    @fly.setter
    def fly(self, value):
        self._fly = types.MethodType(value, self)

    @property
    def quack(self):
        return self._quack

    @quack.setter
    def quack(self, value):
        self._quack = types.MethodType(value, self)


再走一步...

嗯...我们对两个属性都做同样的事情。如果需要,我们可以更进一步。

让我们对其进行概括:

import types

def method_property(name):
    def getter(self):
        return getattr(self, name)
    def setter(self, value):
        setattr(self, name, types.MethodType(value, self))
    return getter, setter

class Duck(object):
    ...
    quack = property(*method_property("_quack"))
    fly = property(*method_property("_fly"))


仅根据需要分配功能-也许在子类的构造函数中。显然,如果您需要更复杂的行为,则可能不需要。但对于简单的事情,我会说这是更好的解决方案。所有子类都为其分配它们,这对于构造函数来说很有意义:

class Duck(object):
    def __init__(self, fly, quack):
        self.fly = fly
        self.quack = quack

    ...

class ModelDuck(Duck):
    def __init__(self):
        Duck.__init__(self, fly_no_way, squeak_quack)
        print "I'm a Model Duck" 


或者,如果您使用的是原始类的实现:

class Duck(object):
    def __init__(self, fly, quack):
        self._fly_behaviour = fly
        self._quack_behaviour = quack

    ...

class ModelDuck(Duck):
    def __init__(self):
        Duck.__init__(self, FlyNoWay(), SqueakQuack())
        print "I'm a Model Duck"



结论

如果将所有这些包装在一起,我们将得到两个实现:



基于函数,并且

基于类。

这些实现消除了重复,将使这种事情在Python整体上更容易使用。

评论


\ $ \ begingroup \ $
哇,谢谢你这么详细的答复。一旦获得足够的代表,我将确保为您+1。同时,您已经给了我很多参考资料,可以应用于我的编程。我还将继续从Head First本书继续研究更多的设计模式。请注意:)
\ $ \ endgroup \ $
– PythonJin
13年1月19日在23:01

\ $ \ begingroup \ $
我仍然在这里添加一些代码。对于基于类的实现,我正在尝试获取__subclasshook__的句柄。在抽象行为中,我们将内置的__subclasshook__定义为函数subclasshook的类方法,该方法在代码顶部定义。因此内置的classmethod应该返回函数subclasshook的Cla​​ss。但是我认为__subclasshook__只能返回True,False或NotImplemented。由于代码有效,我当然会缺少一些东西。
\ $ \ endgroup \ $
– PythonJin
2013年1月20日12:37



\ $ \ begingroup \ $
另外,在顶部函数subclasshook(requried)的for循环中,我不太了解这里的逻辑。我的问题是如果没有的话(在Check .__ mro__中为BaseClass的vars(BaseClass)中的r):.变量r是需要在每种具体行为中实现的抽象方法的名称。因此,在这里,我们检查具体类的__mro__及其所有基类(?),以获取必须实现的抽象方法的名称。我们是否应该检查该方法是否位于__mro__的每个类中,而不仅是每个类?
\ $ \ endgroup \ $
– PythonJin
13年1月20日在12:52

\ $ \ begingroup \ $
@PythonJin为了回应您的第一个评论,subclasshook()返回一个函数,因此当我们调用__subclasshook__ = classmethod(subclasshook([“ fly”]))时,Python会评估subclasshook()调用,并给出classmethod( )返回True,False或NotImplemented作为第一个参数的函数。它不会传递classmethod()到subclasshook()函数本身,而只是传递它返回的值(恰好是另一个函数)。然后,它使其成为一个类方法,并将其分配给__subclasshook__。
\ $ \ endgroup \ $
–漂亮
2013年1月20日16:11



\ $ \ begingroup \ $
@PythonJin为了回应第二点,请考虑调用函数时Python的工作方式。它查找您在该类中调用的函数,如果找不到该函数,则会处理MRO(这是所有基类,并且已按一定顺序删除了重复项-由于Python支持多重继承,所以这不是琐碎的)并检查它,直到找到它为止。如果我们检查它是否在所有类中,那么如果不将其添加到子类中就不可能将其添加到基类中。
\ $ \ endgroup \ $
–漂亮
13年1月20日在16:13