你有点过分了实际上
继承描述的是一种“是”的关系,组合描述的是一种“有”的关系。所以在你的例子中,用构图来表示翅膀和腿之类的属性是非常有意义的,但是鸟、猫和狗都是动物——它们没有“拥有”动物(嗯,它们都有跳蚤,但那是另一个话题)——所以它们应该继承自
Animal
.
而且,大多数鸟的腿都太长了,而且有相当一部分实际上根本不会飞(但有些鸟用腿游泳,而且很有效率)。
在对该属性调用函数之前,首先检查对象是否具有某个属性(在本例中为“legs”)是一种好的做法吗?
真的要看上下文了。作为一般规则,不,这被认为是不好的做法(参见“告诉不要问”和“德米特定律”),但有些情况下,这是合法的。而且,“好”的设计也取决于要解决的问题,我们在这里达到了玩具示例的极限,这些示例永远不能代表现实生活中的用例。
理论上,组合/委派对客户端代码应该是透明的,所以您应该只调用
whatever_animal.walk()
把它干掉。现在您(作为“客户代码”)可能想知道动物不能行走,在这种情况下,非行走动物应该提出一个例外时,收费步行…这也意味着
动物
必须有所有可能的“操作”的默认实现,并且客户机代码必须为“unsupportedAction”(或您想命名它们的方式)异常做好准备。
wrt/implementation,使委派透明可以像使用
__getattr__()
即:
class UnsupportedAction(LookupError):
pass
class Animal(object):
_attributes = ()
def __init__(self, name):
self.name = name
def make_sound(self):
print("silence...")
def __getattr__(self, name):
for att in self._attributes:
if hasattr(att, name):
return getattr(att, name)
else:
raise UnsupportedAction("{} doesn't know how to {}".format(type(self), name))
class Dog(Animal):
_attributes = (Legs(), )
class Bird(Animal):
_attributes = (Legs(), Wings())
这个解决方案的优点是它非常简单而且非常动态。不太好的一点是它既不可检查也不明确。
另一个解决方案是明确授权:
class UnsupportedAction(LookupError):
pass
class Animal(object):
_attributes = ()
def __init__(self, name):
self.name = name
def make_sound(self):
print("silence...")
def walk(self):
return self._resolve_action("walk")
def fly(self):
return self._resolve_action("walk")
# etc
def _resolve_action(self, name):
for att in self._attributes:
if hasattr(att, name):
return getattr(att, name)
else:
raise UnsupportedAction("{} doesn't know how to {}".format(type(self), name))
这是相当详细,少了很多动态,但移动明显,文件化,可读性和可检查性。
在上面的示例中,您实际上可以使用自定义描述符排除冗余代码:
class Action(object):
def __init__(self, name):
self.name = name
def __get__(self, obj, cls):
if obj is None:
return self
return obj._resolve_action(self.name)
def __set__(self, obj, value):
raise AttributeError("Attribute is readonly")
class Animal(object):
_attributes = ()
def __init__(self, name):
self.name = name
def make_sound(self):
print("silence...")
walk = Action("walk")
fly = Action("fly")
# etc
但再一次,如果没有一个真正需要解决的问题,这一切都没有意义,因为这个问题通常定义了正确的解决方案。