第八章:擴展我們的視野
一、對象:原來的觀點和新的觀點
1. 傳統的對象觀點:它們是伴隨有方法的數據。
2. 新的對象觀點:傳統的觀點是正確的,但它是基於實現視角的。更有用的定義是基於概念視角的—對象是擁有責任的實體。這些責任讓對象擁有自己的行爲。也可以想象成“擁有特定行爲德實體”。
按照責任來考慮問題可以使問題更簡單化,因爲這樣可以幫助我們定義對象的公共接口。如果對象有某種責任,就一定有某種途徑來要求它履行自己的責任。但是,這並不對對象內容做任何暗示。關於對象責任的信息甚至可以不在這個對象內部。比如說,假設我擁有一個Shape對象,它的責任是:
l 應該知道自己的位置。
l 應該能夠在屏幕上畫出自己。
l 應該能將自己在屏幕上刪除。
這些責任暗示出下列這些特定的方法(這裏的方法名僅做參考,實際實現時可靈活)必須存在:
l getLocation(…)
l drawShape(…)
l ubDrawShape(…)
但是關於“Shape對象內存在什麼”,並沒有任何暗示。我只關心Shape對象對自己的這些行爲負有責任。它的內部可能有一些屬性,也可能有另一些計算方法,還可能引用其它的對象。
在這裏可以這樣理解:Shape對象的重點工作應該放在實現它的主要方法(責任)上, 對它的內部有些什麼屬性或方法,還是調用了其它類的什麼屬性或方法不去做重點關心。需要什麼就將什麼添加進來。這樣爲你的系統建模提供了靈活性。
二、封裝:原來的觀點和新觀點
封裝應該被想成“熱核形式的隱藏”。換句話說,它可以隱藏數據。但它還可以隱藏實現細節、派生類或其他很多東西。
參看Adapter模式的UML圖,展現了多種封裝:
l 數據的封裝——line、square以及circle對象中的數據都對其他任何對象隱藏。
l 方法的封裝——比如Circle類的display()方法。
l 子類的封裝——Shape類的客戶不會看到line、square或circle類。
l 其他對象的封裝——除了Circle對象之外的任何對象都不知道X_circle對象。
一種類型的封裝這樣實現:抽象類提供多態行爲,於是抽象類的客戶不必知道派生類真正表示的類型。
用這種方式看待封裝的優點是:它給了我一種更好的切分(分解)程序的方法。封裝層成爲我將要設計的接口。
三、發現並封裝變化點
在GoF的書中,GoF給出以下建議:
考慮你的設計中那些是可變的。這個方法與關注引起重新設計的原因剛好相反。它不是考慮什麼會迫使你的設計改變,而是考慮你想要什麼變化卻又不會引起重新設計。最主要的一點是封裝變化的概念,這是許多設計模式的主題。
作爲一種有效的做法,很多設計模式都使用封裝來創建對象之間的分界層——讓設計者可以在分界層的一側做出修改,而不會對另一側產生不良的影響。這促成了層次之間的鬆耦合。理解了這樣的思考方式對Bridge模式的學習和用運是非常重要的。
在OOP中,任何東西都是對象。即使內建數據類型也是對象,他們的行爲就是算法。這一點對學習和理解OO概念是至關重要的。
四、共同點/變化點以及抽象類
在面向對象設計(OOD)的新視角中,我們可以這樣說:
與抽象類的映射
|
討論
|
抽象類à核心約束概念 |
抽象類表現將所有派生類捆綁在一起的核心概念。這個核心概念定義了派生類的共同點。 |
共同點à抽象類使用的 |
共同點定義了我需要使用抽象類 |
變化點à抽象類的派生類 |
從共同點中識別出的變化點成爲抽象類的派生類。 |
規格à抽象類的接口 |
在概念層次與抽象類相對應的接口。 |
五、總結。
一致地處理概念上相同的不同具體類,這是比“特化”更好的使用繼承的方式。
使用對象來包含行爲的變化的概念與用數據成員來包含數據的變化的經驗是想象的。他們都允許封裝(並擴展)被包含的數據或行爲。
總之:
l 發現並封裝變化點。
l 優先使用對象組合,而不是類繼承