傳統的概念
- 將物件視為資料和方法的簡單集合
- 封裝視為資料隱藏
- 繼承 = 特殊化再利用
新的看法
- 將物件視為具有責任的東西
- 封裝視為隱藏一切的一種能力
- 繼承 = 物件分類的一種方法
- 共通性與可變性分析
- 概念視角,規約視角,實作視角抽象類別與衍生類別的關係
- 設計模式與敏捷程式設計方法的差別(冗餘性,可讀性,測試性)
物件
以傳統的方式來看,物件只不過是個資料處理的方法,然而新的看法則是物件是一個具有責任的實體,這兩個差別在於,將物件視為一個責任的實體會讓我們更專注於物件的意圖(責任),而不是如何實作他,我們可以利用這個概念
- 做出初步的設計,這個物件的責任為何,(interface abstract)各種抽象的概念
- 當我們將一切的職責分配完成再來關心實作部分
舉個例子來說
有個需求希望在畫面上顯示/移除形狀,依照這個需求我們可以想像出一個物件需要負責這些事情
- 選擇一種形狀
- 顯示
- 移除
- 取得位置
再進一步仔細想想,是否有發現,所有的形狀物件都一定擁有2,3,4三個必要功能(責任)
所以最後的UML圖會長像是下面的樣子
在此時我們尚未關注細節,我並不曉得方形該怎麼顯示,更不知道三角形該如何取得各點的位置,我不需要瞭解到細節,事實上,這種概念將物件的實作與使用他的物件解耦了。
封裝
傳統的看法為資料的隱藏,然後這種看法侷限性太大了,新的看法則是將封裝視為任何型式的隱藏
- 實作細節
- 衍生類別
- 設計細節
- 實體化設計
讓我們看看上面的UML圖,這是一個adapter pattern的UML圖
Shape shape = new Rectangle();
- 這個動作為類的封裝
shape.Display();
- 實作的封裝
利用adapter pattern 將 third-party Circle封裝進 Circle
- 其他物件的封裝
Rectangle / Triangle/Circle ,詳細的資料只存在自己的class內部,例如一些flag ,各自的location....etc
- 資料的封裝
用這種更寬廣方式來看待封裝,優點就是能夠帶來一種更容易分解程式的方法,而封裝層就成為了設計需要遵循的介面,透過封裝Shape的衍生類別,當我有新的形狀要加入時只需要新增一個class就好了,並不會對其他的程式造成影響。
早期的提倡者,曾將類別得再利用作為巨大的優勢,通常都是建立一個基礎類別之後,然後再從基礎類別衍生出新的特殊化的類別,如下圖
可以想成是普通的圓形以及以虛線畫成的特殊圓形,
利用繼承的好處是我可以快速的利用Circle已經實作的程式碼,但是...........
壞處呢?
- 繼承造成了弱內聚,我再修改Circle的程式碼時,我還必須關注SpecialCircle是否會出現SideEffect。
- 假設我今天希望畫一個虛線版本的三角形呢? 畫虛線的程式碼是否無法再利用,我必須重新建立一個新的物件叫做SpecialTriangle,然後裡頭再重新實作虛線的功能,這種作法減少了程式碼的再利用性
- 同上面描述的,這也減少了物件的延展性,假設今天有更多更多的類別出現,粗線圓形,紅色圓形,漸層圓形,你會發現程式越來越難改,if else判斷越加越多,bug越來越多?!
而另一種方式則是依相同行為分類,使用聚合,關於這部分會在part2的時候做更多的說明~
沒有留言:
張貼留言