构建通用类型- 继承 VS 聚合

Posted 力为

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了构建通用类型- 继承 VS 聚合相关的知识,希望对你有一定的参考价值。

 

继承和聚合的比较GoF[1]做了详尽的阐述,在此偶将从实践的角度用一个例子来提供一种比较通用的解决方案,对继承和聚合做一个适用本案例的选择。此文乃一个案,并不代表两者的绝对优劣,具体问题还是要具体分析。

 

【问题】

CAD或画图软件设计设计中,会存在大量的基本体[2],如line circlearc polyline sphere box等。在组织它们之间的关系的时候,一般会有如下的继承体系:

图表 1 基本体类结构

 

即对每一种基本体,都有一个class与之对应。若有100种基本体,那就得有100classEntity继承而来。

但从这些class本身来看,问题并没有多么严重,毕竟不同的基本体总得有个classobject异或别的什么东西与之对应。一个完整的画图软件总得有序列化机制、总得有个undo/redo机制、总得有个界面显示这些纷纷乱乱的基本体的参数以供交互式操作吧。因此,每个类又得加入save/load操作,undo/redo支持,再加上套UI class[3]。记得N年前做的一个CAX项目中,但为基本体写UI class就花了2W行代码,现在想想有些汗颜。

 

图表 2 属性类与基本体类关系

图表 3 CAD示例

 

在写了N多相似的类后未免让人厌倦,让人有种重构的冲动。实现界面与数据分离可以使用脚本的办法,在此不作赘述

 

【解决方案】

对于一个绘图程序,其基本体主要有两部分内容:参数 + 构建方法。各种基本体之间的差异也存在于此。数据部分可以用一个variant[4]容器统一来描述,通过一个参数ID提取不同的参数。差异最大在于创建实体的方法,即作用于参数的行为。再次抽象后,通用基本体类型层次关系如下:

图表 4 通用基本体类型类结构

 

各种基本体类型都用Primitive统一描述,不再从它继承子类,而是实现不同的创建基本体的BuildMethod。最后用一个Factory模式管理各种BuildMethod,根据与Primitive绑定的BuildMethod ID,创建几何形状。

 

由此所带来的好处是:

1.       Save/Load只需要在Primitive实现

2.       Undo/Redo只需要在Primitive实现

3.       可以从Primitive的参数类型描述中提出GUI表现形式[5]

4.       可扩展性得到增强,新增的类型无需考虑以上三条。

 

由此所带来最大的坏处就是性能上有所损失。从variant得到具体的数据类型毕竟没有直接用原始的数据类型来的高效些。从使用的效果来看,并不会产生用户交互上的延迟。

 

【总结】

         前后两种方法的主要差别在于是使用继承还是聚合,以及是如何使用继承的。二者本身没有优劣之分,但在具体的情景下就需要权衡利弊。

        


--- 力为

 

[1] 《设计模式》

[2] 指基本几何类型。不知道这世界上有多少基本体,在不同的领域中可能基本体的定义也不尽相同。建筑中,可能门,窗都算作是基本体。

[3] boost::serialization 提供了一种非侵入式的序列化方法,值得一试。

Undo/redo的实现可以参考GoFMemento Pattern.

[4] variant的实现有多种方法,boost有两种,参考boost::any boost::variant 的区别

[5] 提取形式亦可参考如何从脚本提取UI的实现方法。

 

以上是关于构建通用类型- 继承 VS 聚合的主要内容,如果未能解决你的问题,请参考以下文章

第八章 聚合与继承

maven-聚合与继承

面向对象设计:共性VS个性-------继承的粒度和聚合的粒度以及类的重构

Maven实战读书笔记:聚合与继承

maven聚合与继承

maven 聚合与继承