最好的不是什么,而是什么时候使用什么的问题。
在“正常”情况下,一个简单的问题就足以确定我们是否需要继承或聚合。
- 如果新的类是或多或少原始类。使用继承。现在,新类是原始类的子类。
- 如果新类必须具有原始类。使用聚合。现在,新班级已成为原始班级的成员。
但是,有一个很大的灰色区域。因此,我们还需要其他一些技巧。
- 如果我们使用了继承(或计划使用它),但是我们仅使用部分接口,或者我们被迫重写许多功能以保持关联逻辑。然后,我们有一种很讨厌的气味,表明我们必须使用聚合。
- 如果我们使用了聚合(或计划使用它),但发现需要复制几乎所有功能。然后,我们有一种指向继承方向的气味。
简而言之。如果不使用或必须更改部分接口以避免不合逻辑的情况,则应使用聚合。如果我们需要几乎所有功能而无需进行重大更改,则仅需要使用继承。如有疑问,请使用聚合。
在我们拥有一个需要原始类功能一部分的类的情况下,另一种可能性是将原始类分为一个根类和一个子类。并让新类从根类继承。但是您应该注意这一点,不要造成不合逻辑的分离。
让我们添加一个例子。我们有一个带有方法的“狗”类:“吃”,“走”,“树皮”,“玩”。
class Dog
Eat;
Walk;
Bark;
Play;
end;
现在,我们需要一个“猫”类,该类需要“吃”,“走”,“冲浪”和“玩耍”。因此,首先尝试从Dog扩展它。
class Cat is Dog
Purr;
end;
看起来不错,但请稍等。这只猫可以吠叫(爱猫的人会因此而杀死我)。吠猫违反了宇宙原理。因此,我们需要重写Bark方法,使其不执行任何操作。
class Cat is Dog
Purr;
Bark = null;
end;
好的,这可以,但是闻起来很不好。因此,让我们尝试一个聚合:
class Cat
has Dog;
Eat = Dog.Eat;
Walk = Dog.Walk;
Play = Dog.Play;
Purr;
end;
好的,这很好。这只猫不再吠叫,甚至没有沉默。但是它仍然有一只想要出来的内部狗。因此,让我们尝试解决方案三:
class Pet
Eat;
Walk;
Play;
end;
class Dog is Pet
Bark;
end;
class Cat is Pet
Purr;
end;
这更干净。没有内部的狗。猫和狗处于同一水平。我们甚至可以引入其他宠物来扩展模型。除非是鱼,否则不会走路。在这种情况下,我们再次需要重构。但这是另一回事了。