2017-09-24 19 views
0

この問題をモデル化する正しい方法を理解できません。 ここで私はあなたに私のコードの最小限のバージョンを与える:2つの実装を1つのクラスにマージするデザ​​インパターン

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

を1つに「TrianglePerimeterCalculator」と「TriangleAreaCalculator」のクラスをマージする優雅な方法はありますが、分離「PerimeterCalculator」を維持し、「AreaCalculator」?

[編集] Kyleがコメントで提案したように、「PerimeterCalculator」と「AreaCalculator」を同時に継承する新しいクラス(「Triangle」と呼ぶこともできます)を作成できますが、 「Triangle」の新しいインスタンスに「PerimeterCalculator」または「AreaCalculator」として動作するように指示できますが、同時に両方ではありません。

+0

あなたは、境界や面積メソッドを持つ、より一般的な「トライアングル」クラスを作成して試すことができます。 – Kyle

+3

Pythonで複数のクラスを継承することができます。クラスCustomClass(BaseClass1、BaseClass2): – Kyle

+0

はい...それは問題を解決しますが、新しい "Triangle"インスタンスが "PerimeterCalculator"または "AreaCalculator"として動作するようにしたいと思いますが、同時に両方ではありません – caspillaga

答えて

1

あなたの質問の編集と明確化の後、別の答えがあります。 Triangleインスタンスを作成し、必要に応じてAreaCalculatorまたはPerimeterCalculatorのように動作させることができます。

このプログラミングパターンは「委任」と呼ばれ、特定の操作を実装する責任が別のオブジェクト(この場合は他のクラスの内部保持されているインスタンス)に引き渡される場合に使用されます。 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())) 

出力:私はカイルとマーティのcommentas /回答にインスピレーションで、自分自身をそれを考え出し

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

私は次のように「トライアングル」をマージされたクラスを作成することができます。

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" 
+2

私は計算機がTriangleクラスから継承するとは思わない –

+1

真... 2番目の考えの後、私はそれが仕事をしているのを見るが、非常に醜い方法である。間違いなく "正しい方法" – caspillaga

関連する問題