继承与聚合
关于如何在面向对象的系统中最好地扩展,增强和重用代码有两种思想流派:
继承:通过创建子类来扩展类的功能。 覆盖子类中的超类成员以提供新功能。 当超类想要一个特定的接口但不知道它的实现时,使方法抽象/虚拟来强制子类“填空”。
聚合:通过采用其他类并将它们组合成一个新类来创建新的功能。 将一个通用接口添加到这个新类中,以实现与其他代码的互操作性。
每种方式的好处,成本和后果是什么? 还有其他的选择吗?
我看到这个辩论经常出现,但我认为它没有被问到Stack Overflow(尽管有一些相关的讨论)。 还有一个惊人的缺乏良好的Google结果。
这不是最好的问题,而是什么时候使用什么。
在'正常'情况下,一个简单的问题就足以发现我们是否需要继承或聚合。
但是,有一个很大的灰色地带。 所以我们需要其他几个技巧。
把它缩短。 如果部分界面未使用或必须更改以避免不合逻辑的情况,我们应该使用聚合。 我们只需要使用继承,如果我们几乎需要所有的功能而不做重大更改。 如有疑问,请使用Aggregation。
对于我们有一个类需要原始类的部分功能的情况,另一种可能性是将原始类拆分为根类和子类。 让新类继承根类。 但你应该注意这一点,而不是造成不合逻辑的分离。
让我们添加一个例子。 我们有一个班'狗'与方法:'吃','走','树皮','玩'。
class Dog
Eat;
Walk;
Bark;
Play;
end;
我们现在需要一个'猫'类,需要'吃','走','普尔'和'玩'。 所以首先尝试从狗中扩展它。
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;
这更清洁。 没有内部的狗。 猫和狗是在同一水平。 我们甚至可以引入其他宠物来扩展模型。 除非是鱼,否则不会走路。 在这种情况下,我们需要重构。 但那是另一回事。
在GOF开始时他们说
赞成继承类继承的对象组合。
这在这里进一步讨论
差异通常表示为“是”和“具有”之间的差异。 继承,“是一种”关系,在Liskov替代原则中很好地概括了。 聚合,“有一个”关系就是这样 - 它表明聚合对象具有聚合对象之一。
进一步的区别也存在 - C ++中的私有继承指示“按照”关系实现,也可以通过(非暴露)成员对象的聚合来建模。
链接地址: http://www.djcxy.com/p/53941.html