【问题标题】:Design Pattern to merge two implementations into one class将两个实现合并为一个类的设计模式
【发布时间】:2017-09-24 23:51:37
【问题描述】:

我无法找出对这个问题建模的正确方法。 这里我给你一个我的代码的简约版本:

# -*- coding: utf-8 -*-
from abc import ABCMeta, abstractmethod

class AreaCalculator():
    __metaclass__ = ABCMeta

    def __init__(self):
        pass

    @abstractmethod
    def getArea(self):
        pass

    def compute(self):
        self.getArea()


class PerimeterCalculator():
    __metaclass__ = ABCMeta

    def __init__(self):
        pass

    @abstractmethod
    def getPerimeter(self):
        pass

    def compute(self):
        self.getPerimeter()


class TriangleAreaCalculator(AreaCalculator):

    def __init__(self):
        AreaCalculator.__init__(self)

    def getArea(self):
        return area

class TrianglePerimeterCalculator(PerimeterCalculator):

    def __init__(self):
        PerimeterCalculator.__init__(self)

    def getPerimeter(self):
        return perimeter



a = TriangleAreaCalculator()
b = TrianglePerimeterCalculator()

有没有一种优雅的方法将“TrianglePerimeterCalculator”和“TriangleAreaCalculator”类合并为一个,但保持“PerimeterCalculator”和“AreaCalculator”分开?

[edit] 正如凯尔在 cmets 中建议的那样,我可以创建一个同时继承自“PerimeterCalculator”和“AreaCalculator”的新类(我们称之为“Triangle”),但我想要的是能够告诉“Triangle”的新实例作为“PerimeterCalculator”或“AreaCalculator”,但不能同时作为两者。

【问题讨论】:

  • 您可以尝试创建一个更通用的具有周长和面积方法的“三角形”类。
  • 您可以从 Python 中的多个类继承。类 CustomClass(BaseClass1, BaseClass2):
  • 是的...解决了这个问题,但我想要一个新的“三角形”实例充当“周长计算器”或“面积计算器”,但不能同时使用两者

标签: python inheritance design-patterns python-2.x


【解决方案1】:

我认为您应该使用的“设计模式”是多重继承。下面是您的代码的修改版本,演示了如何执行此操作(加上一些其他更改以使其实际可运行并且所有类都是新样式)。

from abc import ABCMeta, abstractmethod

class AreaCalculator(object):
    __metaclass__ = ABCMeta

    def __init__(self):
        pass

    @abstractmethod
    def getArea(self):
        pass

    def compute(self):
        self.getArea()


class PerimeterCalculator(object):
    __metaclass__ = ABCMeta

    def __init__(self):
        pass

    @abstractmethod
    def getPerimeter(self):
        pass

    def compute(self):
        self.getPerimeter()


class TriangleAreaCalculator(AreaCalculator):

    def __init__(self):
        super(TriangleAreaCalculator, self).__init__()

    def getArea(self):
        print('TriangleAreaCalculator.getArea() called on instance of {}'.format(
            self.__class__.__name__))
#        return area
        return 13

class TrianglePerimeterCalculator(PerimeterCalculator):

    def __init__(self):
        super(TrianglePerimeterCalculator, self).__init__()

    def getPerimeter(self):
        print('TrianglePerimeterCalculator.getPerimeter() called on instance of {}'.format(
            self.__class__.__name__))
#        return perimeter
        return 42


class MergedCalculator(TriangleAreaCalculator, TrianglePerimeterCalculator):

    def __init__(self):
        super(MergedCalculator, self).__init__()

merged = MergedCalculator()
print('merged.getArea() -> {}'.format(merged.getArea()))
print('merged.getPerimeter() -> {}'.format(merged.getPerimeter()))

输出:

TriangleAreaCalculator.getArea() called on instance of MergedCalculator
merged.getArea() -> 13
TrianglePerimeterCalculator.getPerimeter() called on instance of MergedCalculator
merged.getPerimeter() -> 42

【讨论】:

  • 谢谢马蒂诺。您的答案的问题是“MergedCalculator”现在同时充当 AreaCalculator 和 PerimeterCalculator,但我需要能够实例化充当其中之一的对象,但不能同时充当两者。我非常感谢您的回答并给了您+1,因为它对找出解决方案有很大帮助。谢谢!
  • caspillaga:不客气。如果不是所有时间,什么决定了您的合并课程何时表现得像其他课程中的一个或另一个?换句话说,它如何“知道”行为方式?
【解决方案2】:

在编辑和澄清您的问题之后,这是另一个答案。它允许创建一个单个 Triangle 实例,该实例的行为可以根据需要类似于AreaCalculatorPerimeterCalculator

这种编程模式称为“委托”,用于将实现特定操作的责任移交给不同的对象——在这种情况下,是某个其他类的内部持有的实例。在 Python 中执行此操作的一种常见方法是覆盖类的默认 __getattr__() 方法。

由于您从未对我的其他答案下的评论做出关于究竟是什么控制使用哪种行为的评论,我添加了一个 set_behavior() 方法以允许明确指定它。

from abc import ABCMeta, abstractmethod


class AreaCalculator:
    __metaclass__ = ABCMeta

    def __init__(self):
        pass

    @abstractmethod
    def getArea(self):
        pass

    def compute(self):
        return self.getArea()


class PerimeterCalculator:
    __metaclass__ = ABCMeta

    def __init__(self):
        pass

    @abstractmethod
    def getPerimeter(self):
        pass

    def compute(self):
        return self.getPerimeter()


class TriangleAreaCalculator(AreaCalculator):

    def __init__(self):
        super(TriangleAreaCalculator, self).__init__()

    def getArea(self):
        print('TriangleAreaCalculator.getArea() called')
        area = 13
        return area



class TrianglePerimeterCalculator(PerimeterCalculator):

    def __init__(self):
        super(TrianglePerimeterCalculator, self).__init__()

    def getPerimeter(self):
        print('TrianglePerimeterCalculator.getPerimeter() called')
        perimeter = 42
        return perimeter


class Triangle:

    def __init__(self):
        delegate_classes = TriangleAreaCalculator, TrianglePerimeterCalculator

        # Map delegate classes to instances of themselves.
        self._delegates = {delegate_class: delegate_class()
                            for delegate_class in delegate_classes}

        self.set_behavior(TriangleAreaCalculator)  # Set default delegate.

    def __getattr__(self, attrname):
        # Called only for attributes not defined by this class (or its bases).
        # Retrieve attribute from current behavior delegate class instance.
        return getattr(self._behavior, attrname)

    def set_behavior(self, delegate_class):
        try:
            self._behavior = self._delegates[delegate_class]
        except KeyError:
            raise TypeError("{} isn't a valid {} behavior delegate class"
                              .format(delegate_class, self.__class__.__name__))


if __name__ == '__main__':

    triangle = Triangle()
    # Uses instance's default behavior.
    print('triangle.compute() -> {}'.format(triangle.compute()))

    triangle.set_behavior(TrianglePerimeterCalculator)  # Change behavior.
    print('triangle.compute() -> {}'.format(triangle.compute()))

输出:

TriangleAreaCalculator.getArea() called
triangle.compute() -> 13
TrianglePerimeterCalculator.getPerimeter() called
triangle.compute() -> 42

【讨论】:

    【解决方案3】:

    我自己想通了,灵感来自 Kyle 和 martineau 的评论/答案。

    我可以如下创建一个合并类“Triangle”:

    class Triangle():
    
        def __init__(self):
            pass
    
        def getTriangleArea(self):
            print 'Triangle area'
    
        def getTrianglePerimeter(self):
            print 'Triangle perimeter'
    

    然后修改TriangleAreaCalculator和TrianglePerimeterCalculator如下:

    class TriangleAreaCalculator(AreaCalculator, Triangle):
    
        def __init__(self):
            TriangleCalculator.__init__(self)
            AreaCalculator.__init__(self)
    
        def getArea(self):
            super(TriangleAreaCalculator, self).getTriangleArea()
    
    class TrianglePerimeterCalculator(PerimeterCalculator, Triangle):
    
        def __init__(self):
            TriangleCalculator.__init__(self)
            PerimeterCalculator.__init__(self)
    
        def getPerimeter(self):
            super(TrianglePerimeterCalculator, self).getTrianglePerimeter()
    

    这样,我可以创建一个新的类似三角形的实例,其行为类似于“PerimeterCalculator”或“AreaCalculator”(但不能同时使用两者):

    a = TriangleAreaCalculator()
    b = TrianglePerimeterCalculator()
    
    a.compute() # correctly prints "Triangle area"
    b.compute() # correctly prints "Triangle perimeter"
    

    【讨论】:

    • 我认为计算器不应该从 Triangle 类继承
    • 是的......经过深思熟虑后,我发现它可以完成这项工作,但方式非常丑陋。绝对不是“正确的方式”
    猜你喜欢
    • 1970-01-01
    • 2020-03-22
    • 2012-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-07
    • 1970-01-01
    • 2023-04-06
    相关资源
    最近更新 更多