2017-09-24 29 views
0

我無法弄清楚對這個問題建模的正確方法。 在這裏,我給你我的代碼簡約版本:將兩個實現合併到一個類中的設計模式

# -*- 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

回答

1

下面是對您的問題進行編輯和說明後的另一個答案。它允許創建可根據需要表現得像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 
2

我認爲你應該使用的「設計模式」是多重繼承。下面是你的代碼的一個修改版本,演示了它如何(加上一些其他的改變使它實際可運行並且所有類都是新式的)。

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

0

我理解了它自己,與凱爾和蒂諾的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

相關問題