2017-09-24 3 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"이라고 부름)를 만들 수 있지만 원하는 것은 "Triangle"의 새 인스턴스에 "PerimeterCalculator"또는 "AreaCalculator"로 동작하도록 지시 할 수 있지만 둘 다 동시에 수행 할 수는 없습니다.

+0

당신은 경계 및 면적 메소드가 더 일반적인 "트라이앵글"클래스를 만드는 실험을 할 수 있습니다. – Kyle

+3

파이썬에서 여러 클래스를 상속받을 수 있습니다. CustomClass 클래스 (BaseClass1, BaseClass2) : – Kyle

+0

예 ...그 문제를 해결하지만 새로운 "삼각형"인스턴스 "PerimeterCalculator"또는 "AreaCalculator"로 행동 할 수 있지만 동시에 둘 다 싶지 않다 – caspillaga

답변

1

질문에 대한 편집 및 설명에 대한 또 다른 대답이 있습니다. 필요한 경우 AreaCalculator 또는 PerimeterCalculator과 같이 동작 할 수있는 단일Triangle 인스턴스를 만들 수 있습니다.

이 프로그래밍 패턴을 "위임 (delegation)"이라고하며 특정 작업을 구현하는 책임이 다른 개체 (이 경우에는 다른 클래스의 내부적으로 보유 된 인스턴스)로 전달되는 경우에 사용됩니다. 파이썬에서이 작업을 수행하는 일반적인 방법은 클래스의 기본 __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로 동시에 동작하지만, 동시에 둘 중 하나처럼 동작하는 객체를 instathiate 할 수 있어야했습니다. 해결책을 찾아내는 데 큰 도움이 되었기 때문에 답을 고맙게 생각하고 +1했습니다. 감사! – caspillaga

+0

카스 필라가 : 천만에. 병합 된 클래스가 항상 다른 클래스와 다른 역할을 수행하는 경우를 결정하는 것은 무엇입니까? 다시 말해, 어떤 행동을 취해야 하는지를 어떻게 알 수 있습니까? – martineau

0

.

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

이 방법은, 내가 만들 수있는 새로운 삼각형을 같은 다음과 같이 TriangleAreaCalculator 및 TrianglePerimeterCalculator을 수정 한 후

class Triangle(): 

    def __init__(self): 
     pass 

    def getTriangleArea(self): 
     print 'Triangle area' 

    def getTrianglePerimeter(self): 
     print 'Triangle perimeter' 

을 그리고 :

나는 다음과 같이 병합 된 클래스 "트라이앵글"을 만들 수 있습니다 "PerimeterCalculator"또는 "AreaCalculator"로 작동하는 인스턴스 (동시에 둘 다를 가질 수는 없습니다) :

a = TriangleAreaCalculator() 
b = TrianglePerimeterCalculator() 

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

계산기가 Triangle 클래스에서 상속 받아야한다고 생각하지 않습니다. –

+1

True ... 잠깐 생각한 후에 저는 그것이 일을하지만 아주 추악한 방법으로 생각합니다. 확실히 "올바른 방법" – caspillaga