本周課程主要內容為:C++設計模式簡介、面向對象設計八大原則和DOF-23設計模式中的5中模式,包括template method模式、strategy策略模式、observer觀察者模式、decorator裝飾模式和bridge橋模式。
1、C++設計模式簡介
“每一個模式描述了一個在我們周圍不斷重復發生的問題,以及該問題的解決方案的核心。這樣,你就能一次又一次地使用該方案而不必做重復勞動?!薄狢hristopher Alexander
通常所說的設計模式隱含地表示“面向對象設計模式”,但這并不意味“設計模式”就等于“面向對象設計模式”。
1)c++學習的兩種思維:
底層思維:向下,如何把握機器底層從微觀理解對象構造。
向下:深入理解三大面向對象機制(封裝,隱藏內部實現?;繼承,復用現有代碼; 多態,改寫對象行為)。
抽象思維:向上,如何將我們的周圍世界抽象為程序代碼。
向上:深刻把握面向對象機制所帶來的抽象意義,理解如何使用這些機制來表達現實世界,掌握什么是“好的面向對象設計”。
2)軟件設計的變化性決定了其固有的復雜性,解決復雜性的兩種方法:
分解:人們面對復雜性有一個常見的做法:即分而治之,將大問題分解為多個小問題,將復雜問題分解為多個簡單問題。
抽象:更高層次來講,人們處理復雜性有一個通用的技術,即抽象。由于不能掌握全部的復雜對象,我們選擇忽視它的非本質細節,而去處理泛化和理想化了的對象模型。
3)重點總結
概括起來說就是,以可復用(編譯單位級別的復用,不是源代碼的拷貝粘貼)為目標,以面向對象(適應變化、各負其責)為方法,以分解和抽象的方法解決復雜性。
2、面向對象設計八大原則
將面向對象設計八大原則概括為如圖2所示。
圖2參考于http://www.lxweimin.com/p/02c87c496cd8。
1)設計原則一:依賴倒置原則(DIP)
高層模塊(穩定)不應該依賴于低層模塊(變化),二者都應該依賴于抽象(穩定)。
抽象(穩定)不應該依賴于實現細節(變化),實現細節應該依賴于抽象(穩定)。
課程中講到的例子,如圖3所示,第一種不符合依賴倒置原則,第二種高層模塊和底層模塊均依賴于抽象,符合依賴倒置原則。
2)設計原則二:開放封閉原則(OCP)
對擴展開放,對更改封閉。
類模塊應該是可拓展的,但是不可修改。
3)設計原則三:單一職責原則(SRP)
一個類應該僅有一個引起它變化的原因。
變化的方向隱含著類的責任。
4)設計原則四:Liskov替換原則(LSP)
子類必須能夠替換它們的基類(IS-A)。
繼承表達類型抽象。
5)設計原則五:接口隔離原則(ISP)
不應該強迫客戶程序依賴它們不用的方法。
接口應該小而完備。
6)設計原則六:優先使用對象組合,而不是類繼承
類繼承通常為“白箱復用”,對象組合通常為“黑箱復用”。
繼承在某種程度上破壞了封裝性,子類父類耦合度高(容易誤用)。
而對象組合則只要求被組合的對象具有良好定義的接口,耦合度低。
7)設計原則七:封裝變化點
使用封裝來創建對象之間的分界層,讓設計者可以在分界層的一側進行修改,而不會對另一側產生不良的影響,從而實現層次間的松耦合。
封裝變化點就是一側穩定,一側變化。
8)設計原則八:針對接口編程,而不是針對實現編程
不將變量類型聲明為某個特定的具體類,而是聲明為某個接口。
客戶程序無需獲知對象的具體類型,只需要知道對象所具有的接口。
減少系統類型中各部分的依賴關系,從而實現“高內聚、松耦合”的類型設計方案。
總的來說,將設計原則提升為設計經驗:
設計習語:描述與特定編程語言相關的低層模式,技巧,慣用法。
設計模式:主要描述的是“類與相互通信的對象之間的組織關系,包括它們的角色、職責、協作方式等方面。
架構模式:描述系統中與基本結構組織關系密切的高層模式,包括子系統劃分,職責,以及如何組織它們之間關系的規則。
3、DOF-23設計模式
3.1 分類
1)從目的分類
創建型(Creational)模式:將對象的部分創建工作延遲到子類或者其他對象,從而應對需求變化為對象創建時具體類型實現引來的沖擊。
結構型(Structural)模式:通過類繼承或者對象組合獲得更靈活的結構,從而應對需求變化為對象的結構帶來的沖擊。
行為型(Behavioral)模式:通過類繼承或者對象組合來劃分類與對象間的職責,從而應對需求變化為多個交互的對象帶來的沖擊。
2)從范圍分類
類模式處理類與子類的靜態關系
對象模式處理對象間的動態關系
3)從封裝變化角度分類
3.2 template method模式
template method模式屬于組件協作模式。
template method模式的動機: 在軟件構件過程中,對于某項任務常具有穩定的整體操作結構, 但各個子步驟卻有何多改變的需求,或者由于固有原因(比如框架與應用之間的關系)而無法和任務整體結構同時實現。
emplate method模式定義:定義一個操作中的算法的骨架(穩定),而將一些步驟延遲(變化)到子類中。Template Method使得子類可以不改變(復用)一個算法的結構即可重定義(override重寫)該算法的某些特定步驟。
早綁定:寫得晚的調用寫得早的。
晚早定:寫得早的調用寫得晚的。
應用程序開發人員使用library時,由于library的開發人員已經寫好程序的主流程和部分步驟的具體實現(這些是相對穩定的), 應用開發人員只需對library的類進行繼承,并重寫部分(override)它的成員函數(推薦為protdected類型,不被外界直接調用)即可,這種屬于晚綁定。
對于設計模式的定義:
設計模式的條件是必須有一個穩定點,有穩定和不穩定的成分,設計模式才有用武之地。如果全部都穩定,或是全部都不穩定,那么就不能使用設計模式。
總結:
(1)emplate Method模式是一種非?;A性的設計模式,在面向對象系統中有著大量的應用。它用最簡潔的機制(虛函數的多態性)為很多應用程序框架提供了靈活的擴展點,是代碼復用(二進制運行時刻的復用,不是代碼的復用)方面的基本實現結構。
(2)利用多態性實現晚綁定。不要調用我讓我來調用你 的反向控制結構是tenplate method的典型應用。
(3)具體實現方面,被template method 調用的虛方法可以有實現也可以沒有實現,但一般推薦將他們設置為protected 方法。
3.3 strategy策略模式
strategy策略模式屬于組件協作模式。
strategy策略模式的動機:在軟件構建過程中,某些對象使用的算法可能多種多樣,經常改變,如果將這些算法都編碼到對象中,將會使對象變得異常復雜;而且有時候支持不使用的算法也是一個性能負擔。
strategy策略模式定義:定義一系列算法,把它們一個個封裝起來,并且使它們可互相替換(變化)。該模式使得算法可獨立于使用它的客戶程序(穩定)而變化(擴展,子類化)。
strategy策略模式結構如圖8所示。
總結:
(1)strategy策略模式是以擴展的方式進行變化,典型結構if...else if...,但并不是所有的if...else if...都是strategy策略模式,當if...else if...中內容絕對不變時(如一周中七天),則不是strategy策略模式;
(2)優點:當存在一些無用的算法時, 代碼具有良好的本地性,加載的代碼,就是調用相應的哪個實現方法,但利用if..else if...他們也會加載這些無用的算法到代碼段,影響性能;
(3)使用多態的變量(類內和類外),要用指針。
3.4 observer觀察者模式
observer觀察者模式屬于組件協作模式。
observer觀察者模式的動機:在軟件構建過程中,我們需要為某些對象建立一種“通知依賴關系”——一個對象(目標對象)的狀態發生改變,所有的依賴對象(觀察者對象)都將得到通知。如果這樣的依賴關系過于緊密,將使軟件不能很好地抵御變化。使用面向對象技術,可以將這種依賴關系弱化,并形成一種穩定的依賴關系。從而實現軟件體系結構的松耦合。
observer觀察者模式定義:定義對象間的一種一對多(變化)的依賴關系,以便當一個對象(Subject)的狀態發生改變時,所有依賴于它的對象都得到通知并自動更新。
observer觀察者模式的結構如圖9所示。
總結:
(1)目標發送通知時,無需指定觀察者,通知(可以攜帶通知信息作為參數)會自動傳播。
觀察者自己決定是否需要訂閱通知,目標對象對此一無所知;
(2)Observer模式是基于事情的UI框架中非常常用的設計模式,也是MVC模式的一個重要組成部分;
(3)c++支持多繼承,最好一個是主基類,其他的都是接口類。
3.5 decorator裝飾模式
decorator裝飾模式屬于單一職責模式。
decorator裝飾模式的動機:在某些情況下我們可能會“過度地使用繼承來擴展對象的功能”,由于繼承為類型引入的靜態特質,使得這種擴展方式缺乏靈活性;并且隨著子類的增多(擴展功能的增多),各種子類的組合(擴展功能的組合)會導致更多子類的膨脹。
decorator裝飾模式定義:動態(組合)地給一個對象增加一些額外的職責。就增加功能而言,Decorator模式比生成子類(繼承)更為靈活(消除重復代碼&減少子類個數)。
decorator裝飾模式的結構如圖10所示。
總結:
(1)主體操作和擴展操作應該分開繼承,如圖11所示;
(2)當多個類繼承于同一個類時,多個類的相同的成員應該提到基類中,如果其中有些子類并不使用某些基類數據成員,這時就可以將這些數據成員放入一個中間類(DecoratorStream)中;
(3)通過采用組合而非繼承的手法,Decorator模式實現了在運行時動態擴展對象功能的能力,而且可以根據需要擴展多個功能。避免了使用繼承帶來的“靈活性差”和“多子類衍生問題”;
(4)Decorator類在接口上表現為is-a Component的繼承關系,即Decorator類繼承了Component類所具有的接口。但在實現上又表現為has-a Component的組合關系,即Decorator類又使用了另外一個Component類;
(5)Decorator模式的目的并非解決“多子類衍生的多繼承”問題,Decorator模式應用的要點在于解決“主體類在多個方向上的擴展功能”——是為“裝飾”的含義。
3.6 bridge橋模式
bridge橋模式屬于單一職責模式。
bridge橋模式的動機:由于某些類型的固有的實現邏輯,使得它們具有兩個變化的維度,乃至多個維度的變化。
bridge橋模式定義:將抽象部分(業務功能)與實現部分(平臺實現)分離,使它們都可以獨立地變化。
bridge橋模式的結構如圖12所示。
總結:
(1)Bridge模式使用“對象間的組合關系”解耦了抽象和實現之間固有的綁定關系,使得抽象和實現可以沿著各自的維度來變化。所謂抽象和實現沿著各自維度的變化,即“子類化”它們;
(2)Bridge模式有時候類似于多繼承方案,但是多繼承方案往往違背單一職責原則(即一個類只有一個變化的原因),復用性比較差。Bridge模式是比多繼承方案更好的解決方法;
(3)Bridge模式的應用一般在“兩個非常強的變化維度”,有時一個類也有多于兩個的變化維度,這時可以使用Bridge的擴展模式。