将两个实现合并到一个类中的设计模式

问题描述:

我无法弄清楚对这个问题建模的正确方法。 在这里,我给你我的代码简约版本:将两个实现合并到一个类中的设计模式

# -*- 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”分开吗?由于Kyle在评论中提出建议,我可以创建一个同时继承“PerimeterCalculator”和“AreaCalculator”的新类(我们称之为“Triangle”),但我想要的是能够告诉“三角形”的新实例表现为“周长计算器”或“区域计算器”,但不能同时出现。

+0

您可以创建具有周长和面积的方法更通用的“三角”类实验。 – Kyle

+3

您可以从Python中的多个类继承。 class CustomClass(BaseClass1,BaseClass2): – Kyle

+0

是...解决了这个问题,但是我想要一个新的“三角”实例表现为“PerimeterCalculator”或“AreaCalculator”,但不能同时使用 – caspillaga

下面是对您的问题进行编辑和说明后的另一个答案。它允许创建可根据需要表现得像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 

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

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 
+0

谢谢martineau。你的答案的问题是,“MergedCalculator”现在同时作为AreaCalculator和PerimeterCalculator运行,但我需要能够安装表现为其中一个的对象,但不能同时安装这两个对象。我非常感谢你的回答,并给了你+1,因为这对找出解决方案非常有帮助。谢谢! – caspillaga

+0

caspillaga:不客气。什么决定了你的合并类如果不是所有的时候都像其他类一样行事?换句话说,它是如何“知道”哪种行为方式的? – martineau

我理解了它自己,与凯尔和蒂诺的commentas /答案灵感。

如下,我可以创建一个合并类“三角”:

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() 

这样一来,我可以创造一个新的三角状表现为“周长计算器”或“区域计算器”(但不能同时为两者)的实例:

a = TriangleAreaCalculator() 
b = TrianglePerimeterCalculator() 

a.compute() # correctly prints "Triangle area" 
b.compute() # correctly prints "Triangle perimeter" 
+2

我不认为计算器应该继承三角类 –

+1

真的......经过一秒钟的思考后,我发现它完成了这项工作,但是以一种非常难看的方式。绝对不是“正确的方式” – caspillaga