构建通用类型- 继承 VS 聚合
?
繼承和聚合的比較GoF[1]做了詳盡的闡述,在此偶將從實踐的角度用一個例子來提供一種比較通用的解決方案,對繼承和聚合做一個適用本案例的選擇。此文乃一個案,并不代表兩者的絕對優劣,具體問題還是要具體分析。
?
【問題】
在CAD或畫圖軟件設計設計中,會存在大量的基本體[2],如line、 circle、arc、 polyline、 sphere、 box等。在組織它們之間的關系的時候,一般會有如下的繼承體系:
圖表 1 基本體類結構
?
即對每一種基本體,都有一個class與之對應。若有100種基本體,那就得有100個class從Entity繼承而來。
但從這些class本身來看,問題并沒有多么嚴重,畢竟不同的基本體總得有個class或object異或別的什么東西與之對應。一個完整的畫圖軟件總得有序列化機制、總得有個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的實現可以參考GoF的Memento Pattern.
[4] variant的實現有多種方法,boost有兩種,參考boost::any 與 boost::variant 的區別。
[5] 提取形式亦可參考如何從腳本提取UI的實現方法。
?轉載于:https://www.cnblogs.com/wuwuwu/archive/2008/04/04/6335199.html
總結
以上是生活随笔為你收集整理的构建通用类型- 继承 VS 聚合的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: [导入]C++ GUi 选择
- 下一篇: PDF阅读器Foxit Reader 2
