多重继承
多重继承虽然有一定的优越性,但它为软件引入的复杂性却几乎能掩盖它的所有亮点。
![64c3d4b7ae1c3784d7debcef0057aceb.png](https://i-blog.csdnimg.cn/blog_migrate/126367db1283c2b68ed3f4d7af17775f.jpeg)
![f2cf0a6257efd71ca1458b9e33088e36.png](https://i-blog.csdnimg.cn/blog_migrate/ec8d1f7ba7a0d80eea52888388c49ac4.jpeg)
CISC_CPU、AMD_CPU、MMX_CPU、AMD_K6_2_CPU这四个类组成了一个菱形结构(恐怖菱形)。定义一个AMD_K6_2_CPU的对象实例,该对象中就会包含两份CISC_CPU的拷贝。为了避免出现两份拷贝,ADM_CPU、MMX_CPU两个类从CISC_CPU类继承时,必须使用虚基类继承(class MMX_CPU:virtual public CISC_CPU)。
C++虚基类机制会在编译和运行时耗费更多的空间和时间,把所有的基类都标记为虚基类是不明智的。
程序员在编码时很难决定一个类是否需要采用虚基类机制,程序员必须检查所有的派生类,以确定继承树中是否有菱形结构存在。根据派生类的结构修改基类的代码违法了开闭原则。当继承树非常庞大,菱形结构很多时,同名属性或方法的二义性问题就更难避免了。
如果继承层次很少,设计者能切实避免二义性及“恐怖菱形”的威胁,借助多重继承机制其强大功能优化程序结构就没有什么不好,否则,还是选择其他替代方法。
聚合粒度
使用聚合关系代替继承关系。
![3a67d6962a12fb28465796e746153d79.png](https://i-blog.csdnimg.cn/blog_migrate/34384667f08a0eb272f4284076b14c86.jpeg)
聚合:整体和部分之间的关联关系。
MMX_CPU类改为MXX运算单元类,INTEL_PENTIUM_4_CPU等类聚合了一个MXX运算单元类的对象实例。
Intel Pentium MMX CPU是一个MMX CPU 变成了 Intel Pentium MMX CPU包含一个MMX运算单元。
在这里,聚合关系有效地避免了多重继承的各种麻烦,而且在表述现实问题时也显得更为合理。
使用聚合关系完全能够实现代码复用的目标。
与继承关系的实现代码相比,聚合关系中只多了一些消息转发的代码。
![ddaf96181ec94e41d12d46e452c47086.png](https://i-blog.csdnimg.cn/blog_migrate/6df496bbe13dc8a30008d9ba380a1312.jpeg)
与继承关系相似的是,聚合关系也存在着分解粒度的问题。
聚合关系反映的是整体和部分的关联,这是对现实世界中物质分解的模拟。
现实世界的物质是可以无限分隔的。
在处理聚合的粒度问题时,需要遵循的原则与处理继承粒度时的原则完全一致:模拟现实世界只是手段而不是目的,最终的目的是改善代码结构,提高代码的复用性。
只有从这一原则出发,才能较好地确定概念分解的层次,将聚合的粒度控制在一个既能满足需求又不过分增加软件复杂性的水平上。