Python模板设计模式中有关重写模板方法的问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python模板设计模式中有关重写模板方法的问题相关的知识,希望对你有一定的参考价值。

我违反了Python中的模板设计模式吗?

因此,我有一个用于创建模板方法的基类,我想创建另外两个可用于执行正常函数返回和生成器函数产生的类?

我不得不重写do_something方法并将语句从“ return”更改为关键字“ yield”,或者我应该做另一种替代设计代码更改?

from abc import ABC, abstractmethod

class BaseClass(ABC):
    def do_something(self):
        x = self.do_step1()
        y = self.do_step2()
        return x + y

    @abstractmethod
    def do_step1(self):
        pass

    @abstractmethod
    def do_step2(self):
        pass

class ReturnClass(BaseClass):
    def do_something(self):
        x = self.do_step1()
        y = self.do_step2()
        return x + y

    def do_step1(self):
        return 1

    def do_step2(self):
        return 1

class YieldClass(BaseClass):
    def do_something(self):
        x = self.do_step1()
        y = self.do_step2()
        yield x + y

    def do_step1(self):
        return 2

    def do_step2(self):
        return 2

class ConcreteReturnClass(ReturnClass):
    def do_step1(self):
        return 3

    def do_step2(self):
        return 3

class ConcreteYieldClass(YieldClass):
    def do_step1(self):
        return 4

    def do_step2(self):
        return 4

if __name__ == '__main__':
    return_class = ConcreteReturnClass();
    print(return_class.do_something())

    yield_class = ConcreteYieldClass();
    print(next(yield_class.do_something()))

我的目标是创建一个基类作为模板,但我想重用同一类,并将实现方式从仅返回一个关键字更改为产生生成器函数的方式。我认为我违反了该原则,但我找不到可行的替代方法。

答案

在类BaseClass中,do_something将是您的模板方法,即,它“根据子类重写以提供具体行为的抽象操作定义算法”。在这种情况下,这些操作由抽象方法do_step1do_step2提供,它们需要被子类覆盖。

但在子类ReturnClass中,您已经覆盖了模板方法do_something本身。尽管不被禁止,但是这并不常见。但是在这种情况下,您提供的实现似乎与基类中的实现相同,因此此操作无法完成。我不知道您为什么说您“必须重写do_something方法。”

x = ReturnBase(): print(x.do_something()) // prints 2

无论是否覆盖2,以上打印do_something都是ReturnClass 如果您提供了相同的定义

但是我看到的真正问题是:类或接口代表合同。对于每种方法,都有隐含的前提条件,必须先执行这些条件才能进行调用。对于实现stack并具有pop操作的类,调用pop之前的先决条件是,堆栈中必须至少包含一个元素才能使操作成功。同样,每个方法调用都有一个隐含的后置条件。在最后一个示例中,承诺的是pop方法将使堆栈中的项比调用前少一个。当子类重写基类方法时,它必须遵守隐含的约定,因为它不需要对方法调用提出更强的先决条件,也不承诺在方法返回时提供更少的条件。

在您的示例中,您有两个子类,它们的do_something方法返回的结果类型完全不同。这是对Liskov substitution principle的根本违反,该声明指出,如果S是T的子类型,则可以用S类型的对象替换T类型的对象。而YieldClass类型的对象可能是T的subclass BaseClass,它不是BaseClasssubtype,因为do_something_1(以及BaseClass中)的方法ReturnClass返回int,而do_something_1中的方法YieldClass返回发电机。类YeldClass违反了其基类的约定,并且是非常差的面向对象设计。

以上是关于Python模板设计模式中有关重写模板方法的问题的主要内容,如果未能解决你的问题,请参考以下文章

聊一聊模板方法模式

23种设计模式—— 模板方法

23种设计模式—— 模板方法

23种设计模式—— 模板方法

模板方法模式

模板方法模式