当我设计类并且必须在继承和组合之间进行选择时,我通常使用经验法则:如果关系是"is-a" - 使用继承,如果关系是"has-a" - 使用组合.
总是对的吗?
谢谢.
不 - "是一个"并不总是导致继承.引用得很好的例子是正方形和矩形之间的关系.正方形是一个矩形,但设计从Rectangle类继承Square类的代码是不好的.
我的建议是用Liskov替换原则来增强你的"是一个/有一个"启发式.要检查继承关系是否符合Liskov替换原则,请询问基类的客户端是否可以在子类上运行,而不知道它在子类上运行.当然,必须保留子类的所有属性.
在正方形/矩形示例中,我们必须询问矩形的客户端是否可以在正方形上操作而不知道它是正方形.客户必须知道的是它在矩形上运行.以下函数演示了一个客户端,它假定设置矩形的宽度会使高度保持不变.
void g(Rectangle& r) { r.SetWidth(5); r.SetHeight(4); assert(r.GetWidth() * r.GetHeight()) == 20); }
这个假设适用于矩形,但不适用于正方形.因此该函数不能在一个正方形上运行,因此继承关系违反了Liskov Substitution原则.(顺便说一下 - 这个例子来自链接的文章.)
是的,不是.
线条可以模糊.从OO早期的OO编程的一些非常可怕的例子来看,这没有得到帮助:经理是员工是人.
你必须记住的关于继承的事情是:继承打破了封装.继承是一个实现细节.有关这个主题的各种文章.
最简单的总结方法是:
喜欢成分.
这并不意味着将它用于完全排除继承.这只意味着继承是一种后备立场.
如果关系是"is-a" - 使用继承,如果关系是"has-a" - 使用组合.总是对的吗?
从某种意义上说,是的.但是你必须小心不要引入不必要的,人为的"是一种"关系.
例如,有人可能认为ThickBorderedRectangle 是一个 Rectangle,初看起来似乎很合理,并决定使用继承.但是这种情况更好地描述了一个Rectangle 有一个边框,它可能是也可能不是ThickBorder.在这种情况下,他会更喜欢作曲.
以同样的方式,人们可能会认为ThickBorder 是一个特殊的边界并使用继承; 但是最好说边框有宽度,因此更喜欢构图.
在所有这些模棱两可的案例中,我的经验法则是三思而后行,正如其他人所建议的那样,更喜欢构成而不是继承.