面向对象:慎用“继承”,拥抱“组合”

在c++中,“继承”的特性提高了代码的复用性,使用“继承”,我们可以让子类获得父类的功能,也可以根据自己的需要重载父类的一些函数。“继承”使用起来非常容易,正是因为这样,我们使用的时候也需要注意,要慎用“继承”。

在这篇文章中,假定你已经对面向对象中继承和组合的特性有所了解。正如我们所知道的,继承和组合是c++面向对象编程的主要方式。那么对于若干个有逻辑关系的类,我们应该使用继承还是使用组合呢?这里是我个人的看法。

数据封装

从最佳实践的角度来说,我们的程序设计要尽量做到对数据的封装。对于继承来说,父类的实现对于子类来说是可见的,同时子类又会重写父类的一部分实现,设计模式中认为这是一种破坏父类封装的表现。而对于组合来说,整体类和部分类是相对独立的,各个部分类管理着自己独有的数据,整体类仅仅负责组织这些类,并对外提供服务,整体类和部分类之间不需要关心各自的实现细节。

保持设计单一

继承带来的另一个缺点就是,子类与父类之间耦合紧密,子类的依赖于父类的实现,缺乏独立性。如果父类发生修改,所有的子类都会受到影响。而使用组合就不会有这个问题,因为各个类之间是相互独立的。同时,组合的思想还有助于保持每个类专注于单个任务上,这样可以保持设计的单一,一般也不会出现不可控制的“巨大的类”,而继承就可能带来这些问题。一般我们都会要求累的层次结构不要超过3层,也是为了防止出现一个超级复杂的类。

扩展性

对于继承来说,可以很快的扩展,因为子类可以继承父类的实现,也就可以最大限度的复用代码。但是,继承的扩展虽然快速,但是并不容易,就像上面提到的,子类和父类耦合紧密,如果我们的扩展涉及到父类的内容,那么你很可能会觉得寸步难行,因为父类的更改会影响到他的所有子类,最终也许你也可以完成对应的扩展,但是这样的代价就是增加了系统的复杂度。而对于组合来说就不会有这类问题。

总结

最后还说一句,“慎用继承”并不意味着不考虑继承,“拥抱组合”也不是说在什么时候都要使用组合。更合理的说法是“优先考虑使用组合,而非继承”。

对于我个人来说,继承最好的用法就是通过继承虚基类来实现“多态”,也就是我们常说的“继承接口”,这是程序各个模块之间解耦的最佳方式。

下面是组合与继承的优缺点比较。

组合 继承
封装性 不破坏封装,整体类和局部类之间相互独立 破坏封装,父类和子类耦合紧密,子类的行为依赖于父类
设计难度 简单,每一个类只需要专注于单一的任务 困难,随着继承层次的增加,类的设计会越来越复杂
扩展性 具有较好的扩展性 支持扩展,但是往往以增加系统复杂度为代价
接口 整体类可以对局部类进行封装,提供新的接口 子类不能改变父类的接口

参考资料

面向对象之继承和组合浅谈-破狼-博客园
C++中的继承和组合区别使用

此文有用? 求鼓励!

显示 Gitment 评论