Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python中的模板设计模式关于重写模板方法的问题_Python_Python 3.x_Design Patterns - Fatal编程技术网

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

Python中的模板设计模式关于重写模板方法的问题,python,python-3.x,design-patterns,Python,Python 3.x,Design Patterns,我是否违反了Python中的模板设计模式 所以我有一个基类,在这里我创建了模板方法,我想创建另外两个类,我可以用它们来做一个普通函数返回和生成器函数生成 我必须重写do_something方法,并将语句从“return”更改为关键字“yield”,或者是否还有其他我应该更改的设计代码 from abc import ABC, abstractmethod class BaseClass(ABC): def do_something(self): x = self.do_

我是否违反了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()))

我的目标是创建一个基类作为模板,但我希望重用同一个类,并将实现从只返回一个关键字更改为像生成器函数一样生成。我认为我违反了这个原则,但我无法找到一个可行的替代方案。

在class
BaseClass
中,做一些事情将是你的模板方法,也就是说,它“根据抽象操作定义了一个算法,子类重写该算法以提供具体的行为。”在这种情况下,这些操作是由抽象方法
do_step1
do_step2
提供的,它们需要被子类覆盖

但是在子类
ReturnClass
中,您已经重写了模板方法
dou\u something
本身。虽然没有被禁止,但这并不常见。但在本例中,您提供了一个与基类中的实现完全相同的实现,因此这将一事无成。我不知道你为什么说你“必须重写do_something方法。”

以上打印
2
,无论
do\u something
是否被覆盖为
ReturnClass
,只要您提供了相同的定义

冒着听起来迂腐的风险:

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


在您的示例中,有两个子类的
do\u something
方法返回完全不同类型的结果。这是一个基本的违反,即如果S是T的子类型,则T类型的对象可以替换为S类型的对象。虽然类型为
YieldClass
的对象可以是
BaseClass
的子类,但它不是
BaseClass
的子类型,因为方法
BaseClass
(以及在
ReturnClass
中)返回一个
int
YieldClass
中的方法
dou something\u 1
返回一个生成器。类
YeldClass
违反了基类的约定,是非常糟糕的面向对象设计。简言之,这不再是模板方法模式,而是所谓的反模式。

,您确实打破了基类契约。您还复制了代码,这肯定会导致一些微妙的错误。作为更一般的回答,如果您发现自己重写了模板方法,那么您就有了设计问题

在您当前的示例中(注意:我假设您的真实代码产生的值不止一个——否则我确实没有看到这一点),更好的解决方案是通过直接向基类添加新的响应能力(当然是在不同的方法名称下)并重新实现当前的基基
do_2;southing()
新生成器方法方面的方法,即:

class BaseClass(ABC):

    def iter_do_something(self):
        x = self.do_step1()
        y = self.do_step2()
        yield x + y

    def do_something(self):
        # in real life - assuming 'iter_do_something' yields more
        # than one single value - you would just return the whole list
        # instead of it's first element.
        return list(self.iter_do_something())[0]

    @abstractmethod
    def do_step1(self):
        pass

    @abstractmethod
    def do_step2(self):
        pass

是的,但它们遵循相同的原则。只有一个是屈服的,另一个是正常的返回类…这里有我可以做的选择吗?上面的例子有两个-你只需要使用一个或另一个方法。使用相同的方法返回值或生成器违反了Liskov的替换原则(您的
YieldClass
不是
BaseClass
的适当子类型,您不能用一个替换另一个),复制模板方法逻辑是一种完全违反。现在,如果您发布了一个更能代表您的实际用例的示例(产生一个值有什么意义?)并解释了你为什么要这样做,你可能会得到更好的答案……是的,我认为我违反了它。但我正在考虑一种重用同一个过程的方法。唯一的问题是,一个类似于生成器函数,另一个类似于普通返回函数。你必须重新定义模板方法;这并不完全正确“重用相同的过程。”您可能应该重新问这个问题,提供更多关于您真正想要做什么的实际细节,也许相同的模式或模式的组合可以解决这个问题。
class BaseClass(ABC):

    def iter_do_something(self):
        x = self.do_step1()
        y = self.do_step2()
        yield x + y

    def do_something(self):
        # in real life - assuming 'iter_do_something' yields more
        # than one single value - you would just return the whole list
        # instead of it's first element.
        return list(self.iter_do_something())[0]

    @abstractmethod
    def do_step1(self):
        pass

    @abstractmethod
    def do_step2(self):
        pass