本周課程主要內(nèi)容為:C++設(shè)計模式簡介梦湘、面向?qū)ο笤O(shè)計八大原則和DOF-23設(shè)計模式中的5中模式贱枣,包括template method模式魄藕、strategy策略模式典徊、observer觀察者模式杭煎、decorator裝飾模式和bridge橋模式恩够。
1、C++設(shè)計模式簡介
“每一個模式描述了一個在我們周圍不斷重復(fù)發(fā)生的問題羡铲,以及該問題的解決方案的核心蜂桶。這樣,你就能一次又一次地使用該方案而不必做重復(fù)勞動也切∑嗣模”——Christopher Alexander
通常所說的設(shè)計模式隱含地表示“面向?qū)ο笤O(shè)計模式”,但這并不意味“設(shè)計模式”就等于“面向?qū)ο笤O(shè)計模式”雷恃。
1)c++學(xué)習(xí)的兩種思維:
底層思維:向下疆股,如何把握機器底層從微觀理解對象構(gòu)造。
向下:深入理解三大面向?qū)ο髾C制(封裝倒槐,隱藏內(nèi)部實現(xiàn)?旬痹;繼承,復(fù)用現(xiàn)有代碼讨越; 多態(tài)两残,改寫對象行為)。
抽象思維:向上把跨,如何將我們的周圍世界抽象為程序代碼人弓。
向上:深刻把握面向?qū)ο髾C制所帶來的抽象意義,理解如何使用這些機制來表達現(xiàn)實世界着逐,掌握什么是“好的面向?qū)ο笤O(shè)計”票从。
2)軟件設(shè)計的變化性決定了其固有的復(fù)雜性,解決復(fù)雜性的兩種方法:
分解:人們面對復(fù)雜性有一個常見的做法:即分而治之滨嘱,將大問題分解為多個小問題峰鄙,將復(fù)雜問題分解為多個簡單問題。
抽象:更高層次來講太雨,人們處理復(fù)雜性有一個通用的技術(shù)吟榴,即抽象。由于不能掌握全部的復(fù)雜對象囊扳,我們選擇忽視它的非本質(zhì)細節(jié)吩翻,而去處理泛化和理想化了的對象模型。
3)重點總結(jié)
概括起來說就是锥咸,以可復(fù)用(編譯單位級別的復(fù)用狭瞎,不是源代碼的拷貝粘貼)為目標,以面向?qū)ο?/b>(適應(yīng)變化搏予、各負其責(zé))為方法熊锭,以分解和抽象的方法解決復(fù)雜性。
2、面向?qū)ο笤O(shè)計八大原則
將面向?qū)ο笤O(shè)計八大原則概括為如圖2所示碗殷。
圖2參考于http://www.reibang.com/p/02c87c496cd8精绎。
1)設(shè)計原則一:依賴倒置原則(DIP)
高層模塊(穩(wěn)定)不應(yīng)該依賴于低層模塊(變化),二者都應(yīng)該依賴于抽象(穩(wěn)定)锌妻。
抽象(穩(wěn)定)不應(yīng)該依賴于實現(xiàn)細節(jié)(變化)代乃,實現(xiàn)細節(jié)應(yīng)該依賴于抽象(穩(wěn)定)。
課程中講到的例子仿粹,如圖3所示搁吓,第一種不符合依賴倒置原則,第二種高層模塊和底層模塊均依賴于抽象吭历,符合依賴倒置原則堕仔。
2)設(shè)計原則二:開放封閉原則(OCP)
對擴展開放,對更改封閉毒涧。
類模塊應(yīng)該是可拓展的贮预,但是不可修改贝室。
3)設(shè)計原則三:單一職責(zé)原則(SRP)
一個類應(yīng)該僅有一個引起它變化的原因契讲。
變化的方向隱含著類的責(zé)任。
4)設(shè)計原則四:Liskov替換原則(LSP)
子類必須能夠替換它們的基類(IS-A)滑频。
繼承表達類型抽象捡偏。
5)設(shè)計原則五:接口隔離原則(ISP)
不應(yīng)該強迫客戶程序依賴它們不用的方法。
接口應(yīng)該小而完備峡迷。
6)設(shè)計原則六:優(yōu)先使用對象組合银伟,而不是類繼承
類繼承通常為“白箱復(fù)用”,對象組合通常為“黑箱復(fù)用”绘搞。
繼承在某種程度上破壞了封裝性彤避,子類父類耦合度高(容易誤用)。
而對象組合則只要求被組合的對象具有良好定義的接口夯辖,耦合度低琉预。
7)設(shè)計原則七:封裝變化點
使用封裝來創(chuàng)建對象之間的分界層,讓設(shè)計者可以在分界層的一側(cè)進行修改蒿褂,而不會對另一側(cè)產(chǎn)生不良的影響圆米,從而實現(xiàn)層次間的松耦合。
封裝變化點就是一側(cè)穩(wěn)定啄栓,一側(cè)變化娄帖。
8)設(shè)計原則八:針對接口編程,而不是針對實現(xiàn)編程
不將變量類型聲明為某個特定的具體類昙楚,而是聲明為某個接口近速。
客戶程序無需獲知對象的具體類型,只需要知道對象所具有的接口。
減少系統(tǒng)類型中各部分的依賴關(guān)系数焊,從而實現(xiàn)“高內(nèi)聚永淌、松耦合”的類型設(shè)計方案。
總的來說佩耳,將設(shè)計原則提升為設(shè)計經(jīng)驗:
設(shè)計習(xí)語:描述與特定編程語言相關(guān)的低層模式遂蛀,技巧,慣用法干厚。
設(shè)計模式:主要描述的是“類與相互通信的對象之間的組織關(guān)系李滴,包括它們的角色、職責(zé)蛮瞄、協(xié)作方式等方面所坯。
架構(gòu)模式:描述系統(tǒng)中與基本結(jié)構(gòu)組織關(guān)系密切的高層模式,包括子系統(tǒng)劃分挂捅,職責(zé)芹助,以及如何組織它們之間關(guān)系的規(guī)則。
3闲先、DOF-23設(shè)計模式
3.1 分類
1)從目的分類
創(chuàng)建型(Creational)模式:將對象的部分創(chuàng)建工作延遲到子類或者其他對象状土,從而應(yīng)對需求變化為對象創(chuàng)建時具體類型實現(xiàn)引來的沖擊。
結(jié)構(gòu)型(Structural)模式:通過類繼承或者對象組合獲得更靈活的結(jié)構(gòu)伺糠,從而應(yīng)對需求變化為對象的結(jié)構(gòu)帶來的沖擊蒙谓。
行為型(Behavioral)模式:通過類繼承或者對象組合來劃分類與對象間的職責(zé),從而應(yīng)對需求變化為多個交互的對象帶來的沖擊训桶。
2)從范圍分類
類模式處理類與子類的靜態(tài)關(guān)系
對象模式處理對象間的動態(tài)關(guān)系
3)從封裝變化角度分類
3.2 template method模式
template method模式屬于組件協(xié)作模式累驮。
template method模式的動機: 在軟件構(gòu)件過程中,對于某項任務(wù)常具有穩(wěn)定的整體操作結(jié)構(gòu)舵揭, 但各個子步驟卻有何多改變的需求谤专,或者由于固有原因(比如框架與應(yīng)用之間的關(guān)系)而無法和任務(wù)整體結(jié)構(gòu)同時實現(xiàn)。
emplate method模式定義:定義一個操作中的算法的骨架(穩(wěn)定)午绳,而將一些步驟延遲(變化)到子類中置侍。Template Method使得子類可以不改變(復(fù)用)一個算法的結(jié)構(gòu)即可重定義(override重寫)該算法的某些特定步驟。
早綁定:寫得晚的調(diào)用寫得早的箱叁。
晚早定:寫得早的調(diào)用寫得晚的墅垮。
應(yīng)用程序開發(fā)人員使用library時,由于library的開發(fā)人員已經(jīng)寫好程序的主流程和部分步驟的具體實現(xiàn)(這些是相對穩(wěn)定的)耕漱, 應(yīng)用開發(fā)人員只需對library的類進行繼承算色,并重寫部分(override)它的成員函數(shù)(推薦為protdected類型,不被外界直接調(diào)用)即可螟够,這種屬于晚綁定灾梦。
對于設(shè)計模式的定義:
設(shè)計模式的條件是必須有一個穩(wěn)定點峡钓,有穩(wěn)定和不穩(wěn)定的成分,設(shè)計模式才有用武之地若河。如果全部都穩(wěn)定能岩,或是全部都不穩(wěn)定,那么就不能使用設(shè)計模式萧福。
總結(jié):
(1)emplate Method模式是一種非忱椋基礎(chǔ)性的設(shè)計模式,在面向?qū)ο笙到y(tǒng)中有著大量的應(yīng)用鲫忍。它用最簡潔的機制(虛函數(shù)的多態(tài)性)為很多應(yīng)用程序框架提供了靈活的擴展點膏燕,是代碼復(fù)用(二進制運行時刻的復(fù)用,不是代碼的復(fù)用)方面的基本實現(xiàn)結(jié)構(gòu)悟民。
(2)利用多態(tài)性實現(xiàn)晚綁定坝辫。不要調(diào)用我讓我來調(diào)用你 的反向控制結(jié)構(gòu)是tenplate method的典型應(yīng)用。
(3)具體實現(xiàn)方面射亏,被template method 調(diào)用的虛方法可以有實現(xiàn)也可以沒有實現(xiàn)近忙,但一般推薦將他們設(shè)置為protected 方法。
3.3 strategy策略模式
strategy策略模式屬于組件協(xié)作模式智润。
strategy策略模式的動機:在軟件構(gòu)建過程中及舍,某些對象使用的算法可能多種多樣,經(jīng)常改變做鹰,如果將這些算法都編碼到對象中击纬,將會使對象變得異常復(fù)雜鼎姐;而且有時候支持不使用的算法也是一個性能負擔(dān)钾麸。
strategy策略模式定義:定義一系列算法,把它們一個個封裝起來炕桨,并且使它們可互相替換(變化)饭尝。該模式使得算法可獨立于使用它的客戶程序(穩(wěn)定)而變化(擴展,子類化)献宫。
strategy策略模式結(jié)構(gòu)如圖8所示钥平。
總結(jié):
(1)strategy策略模式是以擴展的方式進行變化,典型結(jié)構(gòu)if...else if...姊途,但并不是所有的if...else if...都是strategy策略模式涉瘾,當(dāng)if...else if...中內(nèi)容絕對不變時(如一周中七天),則不是strategy策略模式捷兰;
(2)優(yōu)點:當(dāng)存在一些無用的算法時立叛, 代碼具有良好的本地性,加載的代碼贡茅,就是調(diào)用相應(yīng)的哪個實現(xiàn)方法秘蛇,但利用if..else if...他們也會加載這些無用的算法到代碼段其做,影響性能;
(3)使用多態(tài)的變量(類內(nèi)和類外)赁还,要用指針妖泄。
3.4 observer觀察者模式
observer觀察者模式屬于組件協(xié)作模式。
observer觀察者模式的動機:在軟件構(gòu)建過程中艘策,我們需要為某些對象建立一種“通知依賴關(guān)系”——一個對象(目標對象)的狀態(tài)發(fā)生改變蹈胡,所有的依賴對象(觀察者對象)都將得到通知。如果這樣的依賴關(guān)系過于緊密朋蔫,將使軟件不能很好地抵御變化审残。使用面向?qū)ο蠹夹g(shù),可以將這種依賴關(guān)系弱化斑举,并形成一種穩(wěn)定的依賴關(guān)系搅轿。從而實現(xiàn)軟件體系結(jié)構(gòu)的松耦合。
observer觀察者模式定義:定義對象間的一種一對多(變化)的依賴關(guān)系富玷,以便當(dāng)一個對象(Subject)的狀態(tài)發(fā)生改變時璧坟,所有依賴于它的對象都得到通知并自動更新。
observer觀察者模式的結(jié)構(gòu)如圖9所示赎懦。
總結(jié):
(1)目標發(fā)送通知時雀鹃,無需指定觀察者,通知(可以攜帶通知信息作為參數(shù))會自動傳播励两。
觀察者自己決定是否需要訂閱通知黎茎,目標對象對此一無所知;
(2)Observer模式是基于事情的UI框架中非常常用的設(shè)計模式当悔,也是MVC模式的一個重要組成部分傅瞻;
(3)c++支持多繼承,最好一個是主基類盲憎,其他的都是接口類嗅骄。
3.5 decorator裝飾模式
decorator裝飾模式屬于單一職責(zé)模式。
decorator裝飾模式的動機:在某些情況下我們可能會“過度地使用繼承來擴展對象的功能”饼疙,由于繼承為類型引入的靜態(tài)特質(zhì)溺森,使得這種擴展方式缺乏靈活性;并且隨著子類的增多(擴展功能的增多)窑眯,各種子類的組合(擴展功能的組合)會導(dǎo)致更多子類的膨脹屏积。
decorator裝飾模式定義:動態(tài)(組合)地給一個對象增加一些額外的職責(zé)。就增加功能而言磅甩,Decorator模式比生成子類(繼承)更為靈活(消除重復(fù)代碼&減少子類個數(shù))炊林。
decorator裝飾模式的結(jié)構(gòu)如圖10所示。
總結(jié):
(1)主體操作和擴展操作應(yīng)該分開繼承更胖,如圖11所示铛铁;
(2)當(dāng)多個類繼承于同一個類時隔显,多個類的相同的成員應(yīng)該提到基類中,如果其中有些子類并不使用某些基類數(shù)據(jù)成員饵逐,這時就可以將這些數(shù)據(jù)成員放入一個中間類(DecoratorStream)中括眠;
(3)通過采用組合而非繼承的手法,Decorator模式實現(xiàn)了在運行時動態(tài)擴展對象功能的能力倍权,而且可以根據(jù)需要擴展多個功能掷豺。避免了使用繼承帶來的“靈活性差”和“多子類衍生問題”;
(4)Decorator類在接口上表現(xiàn)為is-a Component的繼承關(guān)系薄声,即Decorator類繼承了Component類所具有的接口当船。但在實現(xiàn)上又表現(xiàn)為has-a Component的組合關(guān)系,即Decorator類又使用了另外一個Component類默辨;
(5)Decorator模式的目的并非解決“多子類衍生的多繼承”問題德频,Decorator模式應(yīng)用的要點在于解決“主體類在多個方向上的擴展功能”——是為“裝飾”的含義。
3.6 bridge橋模式
bridge橋模式屬于單一職責(zé)模式缩幸。
bridge橋模式的動機:由于某些類型的固有的實現(xiàn)邏輯奶浦,使得它們具有兩個變化的維度榔组,乃至多個維度的變化娘荡。
bridge橋模式定義:將抽象部分(業(yè)務(wù)功能)與實現(xiàn)部分(平臺實現(xiàn))分離喉祭,使它們都可以獨立地變化。
bridge橋模式的結(jié)構(gòu)如圖12所示爆办。
總結(jié):
(1)Bridge模式使用“對象間的組合關(guān)系”解耦了抽象和實現(xiàn)之間固有的綁定關(guān)系难咕,使得抽象和實現(xiàn)可以沿著各自的維度來變化。所謂抽象和實現(xiàn)沿著各自維度的變化距辆,即“子類化”它們余佃;
(2)Bridge模式有時候類似于多繼承方案,但是多繼承方案往往違背單一職責(zé)原則(即一個類只有一個變化的原因)挑格,復(fù)用性比較差咙冗。Bridge模式是比多繼承方案更好的解決方法沾歪;
(3)Bridge模式的應(yīng)用一般在“兩個非常強的變化維度”漂彤,有時一個類也有多于兩個的變化維度,這時可以使用Bridge的擴展模式灾搏。