将两个实现合并到一个类中的设计模式
我无法弄清楚对这个问题建模的正确方法。 在这里,我给你我的代码简约版本:将两个实现合并到一个类中的设计模式
# -*- 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”),但我想要的是能够告诉“三角形”的新实例表现为“周长计算器”或“区域计算器”,但不能同时出现。
下面是对您的问题进行编辑和说明后的另一个答案。它允许创建可根据需要表现得像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
我认为你应该使用的“设计模式”是多重继承。下面是你的代码的一个修改版本,演示了它如何(加上一些其他的改变使它实际可运行并且所有类都是新式的)。
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
谢谢martineau。你的答案的问题是,“MergedCalculator”现在同时作为AreaCalculator和PerimeterCalculator运行,但我需要能够安装表现为其中一个的对象,但不能同时安装这两个对象。我非常感谢你的回答,并给了你+1,因为这对找出解决方案非常有帮助。谢谢! – caspillaga
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"
我不认为计算器应该继承三角类 –
真的......经过一秒钟的思考后,我发现它完成了这项工作,但是以一种非常难看的方式。绝对不是“正确的方式” – caspillaga
您可以创建具有周长和面积的方法更通用的“三角”类实验。 – Kyle
您可以从Python中的多个类继承。 class CustomClass(BaseClass1,BaseClass2): – Kyle
是...解决了这个问题,但是我想要一个新的“三角”实例表现为“PerimeterCalculator”或“AreaCalculator”,但不能同时使用 – caspillaga