1.設(shè)計模式簡介
課程目標(biāo)
- 松耦合設(shè)計思想
- 面向?qū)ο笤O(shè)計原則
- 重構(gòu)技法改善設(shè)計
- GOF核心設(shè)計模式
設(shè)計模式
- 不斷重復(fù)發(fā)生的問題
- 解決方案的核心
- 重復(fù)使用
面向?qū)ο螅?/h4>
- 向下,如何把握機(jī)器底層從微觀理解對象構(gòu)造
- 語言構(gòu)造
- 編譯轉(zhuǎn)換
- 內(nèi)存模型
- 運行時機(jī)制
- 向上:如何將我們周圍的世界抽象為程序代碼
- 面向?qū)ο?/li>
- 組件封裝
- 設(shè)計模式
- 架構(gòu)模式
- 語言構(gòu)造
- 編譯轉(zhuǎn)換
- 內(nèi)存模型
- 運行時機(jī)制
深入理解面向?qū)ο螅?/h4>
- 向下:三大面向?qū)ο髾C(jī)制
- 封裝,隱藏內(nèi)部實現(xiàn)
- 繼承,復(fù)用現(xiàn)有代碼
- 多態(tài),改寫對象行為
- 向上:深刻把握面向?qū)ο髾C(jī)制所帶來的抽象意義,理解如何使用這些機(jī)制來表達(dá)現(xiàn)實世界,掌握什么是“好的面向?qū)ο笤O(shè)計”
- 封裝,隱藏內(nèi)部實現(xiàn)
- 繼承,復(fù)用現(xiàn)有代碼
- 多態(tài),改寫對象行為
軟件設(shè)計復(fù)雜的根本原因:變化
- 客戶需求
- 技術(shù)平臺
- 開發(fā)團(tuán)隊
- 市場環(huán)境
- ...
解決問題的思維模型:
- 分解:將大問題分解為多個小問題,將復(fù)雜問題分解為多個簡單的問題(C語言為代表)
- 抽象:由于不能掌握全部的復(fù)雜對象,我們選擇忽視它的非本質(zhì)細(xì)節(jié)而去處理泛化和理想化了的對象模型
什么是好的軟件設(shè)計?軟件設(shè)計的金科玉律:復(fù)用!
2.面向?qū)ο笤O(shè)計原則
變化是復(fù)用的天敵!面向?qū)ο笤O(shè)計最大的優(yōu)勢在于:抵御變化!
重新認(rèn)識面向?qū)ο螅?/h4>
- 隔離變化:能將變化所帶來的影響減到最小。
- 各司其職:面向?qū)ο蟮姆绞綇?qiáng)調(diào)個各類的“職責(zé)”,新增加的類不會影響原有類。
- 對象是什么?
- 語言:對象封裝了代碼和數(shù)據(jù)。
- 規(guī)格:對象是一系列可被使用的公共接口。
- 概念:對象是某種擁有責(zé)任的抽象。
- 語言:對象封裝了代碼和數(shù)據(jù)。
- 規(guī)格:對象是一系列可被使用的公共接口。
- 概念:對象是某種擁有責(zé)任的抽象。
面向?qū)ο笤O(shè)計原則:
設(shè)計原則比設(shè)計模式更為重要,在發(fā)展的過程中,設(shè)計模式可能在變,但是設(shè)計模式依賴于設(shè)計原則,設(shè)計原則是評判設(shè)計模式的標(biāo)準(zhǔn)。
依賴導(dǎo)致原則(DIP)
- 高層模塊(穩(wěn)定)不應(yīng)該依賴于低層模塊(變化),二者都應(yīng)該依賴于抽象(穩(wěn)定)。
- 抽象(穩(wěn)定)不應(yīng)該依賴于實現(xiàn)細(xì)節(jié)(變化),實現(xiàn)細(xì)節(jié)能改依賴于抽象(抽象)。
開放封閉原則(OCP)
- 對擴(kuò)展開放,對更改封閉
- 類模塊應(yīng)該是可擴(kuò)展的,但是不可修改。
單一職責(zé)原則(SRP)
關(guān)于類的職責(zé),思考時必須慎之又慎!
- 一個類應(yīng)該僅有一個引起它變化的原因。
- 變化的方向隱含著類的責(zé)任。
Liskov替換原則(LSP)
- 子類必須能夠替換他們的基類(IS-A)。
- 繼承表達(dá)類型抽象。
接口隔離原則(ISP)
- 不應(yīng)該強(qiáng)迫客戶程序依賴他們不用的方法。
- 接口應(yīng)該小而完備。
優(yōu)先使用對象組合,而不是類繼承
- 類繼承通常為“白箱復(fù)用”,對象組合通常為“黑箱復(fù)用”。
- 繼承在某種程度上破壞了封裝性,子類父類耦合度高。
- 對象組合則只要求被組合的對象具有良好定義的接口,耦合度底。
封裝變化點
- 使用封裝創(chuàng)建對象之間的分界層,讓設(shè)計者可以在分界層的一側(cè)進(jìn)行修改,而不會對另一側(cè)產(chǎn)生不良的影響,從而實現(xiàn)層次間的松耦合。
針對接口編程,而不是針對實現(xiàn)編程
- 不將變量類型聲明為某個特定的具體類,而是聲明為某個接口。
- 客戶程序無需獲知對象的具體類型,只需要知道對象所具有的接口。
- 減少系統(tǒng)中各部分的依賴關(guān)系,從而實現(xiàn)“高內(nèi)聚,松耦合”的類型設(shè)計方案。
面向接口設(shè)計,產(chǎn)業(yè)強(qiáng)盛的標(biāo)志:接口標(biāo)準(zhǔn)化
GOF-23模式分類
從目的
- 創(chuàng)建型(Creational)
- 結(jié)構(gòu)型(Structural)
- 行為型(Behavioral)
從范圍(實現(xiàn)手段)
- 類模式(類和子類的靜態(tài)關(guān)系)(繼承方案)
- 對象模式(對象間的動態(tài)關(guān)系)(組合方案)
從封裝變化角度對模式分類:
重構(gòu)獲得模式 Refactoring to Patterns
- 應(yīng)對變化,提高復(fù)用
- 發(fā)現(xiàn)變化,在變化點處應(yīng)用設(shè)計模式。什么時候、什么地點應(yīng)用設(shè)計模式比理解設(shè)計模式結(jié)構(gòu)本身更為重要
- 設(shè)計模式應(yīng)用不宜先入為主。沒有一步到位的設(shè)計模式。
重構(gòu)關(guān)鍵技巧:
- 靜態(tài) -> 動態(tài)
- 早綁定 -> 晚綁定
- 繼承 -> 組合
- 編譯器依賴 -> 運行時依賴
- 緊耦合 -> 松耦合
3.組件協(xié)作模式
- 現(xiàn)代軟件專業(yè)分工之后的第一個結(jié)果是“框架與應(yīng)用程序的劃分”,“組件協(xié)作”模式通過晚期綁定,來實現(xiàn)框架與應(yīng)用程序之間的松耦合,是二者之間協(xié)作時常用的模式。
- 典型模式
- Template Method
- Strategy
- Observer/Event
Template Method
動機(jī)
- 在軟件構(gòu)件過程中,對于某一項任務(wù),它常常有穩(wěn)定的整體操作結(jié)構(gòu),但是個個自步驟卻有很多改變的需求,或者由于固有的原因(比如框架與應(yīng)用之間的關(guān)系)而無法和任務(wù)的整體結(jié)構(gòu)同時實現(xiàn)。
- 定義一個操作中的算法的骨架(穩(wěn)定),而將一些步驟延遲(變化)到子類中。Template Method使得子類可以不改變(復(fù)用)一個算法的結(jié)構(gòu)即可重定義(override重寫)該算法的某些特定步驟。
結(jié)構(gòu)
要點總結(jié)
- Template Method模式是一種非常基礎(chǔ)性的設(shè)計模式,在面向?qū)ο笙到y(tǒng)中有著大量的應(yīng)用。使用虛函數(shù)的多態(tài)性為很多應(yīng)用程序框架提供了靈活的擴(kuò)展點,是代碼復(fù)用方面的基本實現(xiàn)結(jié)構(gòu)。
- 除了可以靈活對子步驟的變化外,“不要調(diào)用我,讓我來調(diào)用你”的反向控制結(jié)構(gòu)是Template Method的典型應(yīng)用。
- 在實現(xiàn)時,被Template Method調(diào)用的虛方法可以具有實現(xiàn),也可以沒有任何實現(xiàn)(抽象方法、純虛方法),但一般推薦將他們設(shè)置為protected方法。
Strategy
動機(jī)
- 在軟件構(gòu)件過程中,某些對象使用的算法可能多種多樣,經(jīng)常改變,如果將這些算法都編碼到對象中,將會使對象變得異常負(fù)責(zé),而且有時候支持不適用的算法也是一個性能負(fù)擔(dān)。
- 定義一系列算法,把他們一個個封裝起來,并且使他們可以相互替換(變化),該模式使得算法可獨立于使用它的客戶程序(穩(wěn)定)而變化(擴(kuò)展,子類化)。
結(jié)構(gòu)
要點總結(jié)
- Strategy及其子類為組件提供了一系列可重用的算法,從而可以使得類型在運行時方便地根據(jù)需要在各個算法之間進(jìn)行切換。
- Strategy模式提供了用條件判斷語句以外的另一種選擇,消除條件判斷語句,就是在解耦合。含有許多條件判斷語句的代碼通常都需要使用Strategy模式。
- 如果Strategy對象沒有實例變量,那么各個上下文可以共享同一個Strategy對象,從而節(jié)省對象開銷。
Observer/Event
動機(jī)
- 在軟件構(gòu)建過程中,我們需要為某些對象建立一種“通知依賴關(guān)系”——一個對象(目標(biāo)對象)的狀態(tài)發(fā)生改變,所有的依賴對象(觀察者對象)都將得到通知。
- 定義對象間的一種一對多(變化)的依賴關(guān)系,以便當(dāng)一個對象(Subject)的狀態(tài)發(fā)生改變時,所有依賴于它的對象都得到通知并自動更新。
結(jié)構(gòu)
要點總結(jié)
- 使用面向?qū)ο蟮某橄螅琌bserver模式使得我們可以獨立地改變(改變時,兩者不相互影響)目標(biāo)與觀察者,從而使二者之間的依賴關(guān)系松耦合。
- 目標(biāo)發(fā)送通知時,無需指定觀察者,通知會自動傳播。
- 觀察者自己決定是否需要訂閱通知,目標(biāo)對象對此一無所知。
- Observer模式是基于事件的UI框架中非常常用的設(shè)計模式,是MVC的一個重要組成部分。
最關(guān)鍵的是抽象的通知依賴關(guān)系。
4.單一職責(zé)
- 在軟件設(shè)計中,如果責(zé)任劃分不清楚,使用繼承得到的結(jié)果往往都是隨著需求變化,子類急劇膨脹,同時充斥著很多重復(fù)代碼,這時候的關(guān)鍵是劃清責(zé)任。
- 典型模式
- Decorator
- Bridge
Decorator 裝飾模式
動機(jī)
- 在某些情況下,我們可能會“過度的使用繼承來擴(kuò)展對象的功能”,由于繼承為類型引入的靜態(tài)特性,使得這種擴(kuò)展方式缺少靈活性,并且隨著子類的增多,各種子類的組合會導(dǎo)致更多子類的膨脹。
- 動態(tài)(組合)的給一個對象增加一些額外的職責(zé),就增加功能而言,Decorator模式比生成子類(繼承)更為靈活(消除重復(fù)代碼&減少子類個數(shù))。
結(jié)構(gòu)
要點總結(jié)
- 通過組合而非繼承的手法,Decorator模式實現(xiàn)了在運行時動態(tài)擴(kuò)展對象功能的能力,可根據(jù)需要擴(kuò)展多個功能。
- Decorator類在接口上表現(xiàn)為is-a Component的繼承關(guān)系,即Decorator類繼承了Component類的所有接口。但是在實現(xiàn)上又表現(xiàn)為has-a Component的組合關(guān)系,即Decorator類又使用了另一個Component類。(以后當(dāng)看到一個類,同時繼承和組合父類,那么很多時候就是使用了Decorator設(shè)計模式。)
- Decorator模式應(yīng)用的要點在于解決“主體類在多個方向上的擴(kuò)展功能”----即為“裝飾”。
Bridge 橋模式
動機(jī)
- 由于某些類型的固有的實現(xiàn)邏輯,使得它們具有連個變化的維度,乃至多個維度的變化。
- 將抽象部分(業(yè)務(wù)功能)與實現(xiàn)部分(平臺實現(xiàn))分離,使它們可以獨立變化。
結(jié)構(gòu)
要點總結(jié)
- Bridge模式使用“對象間的組合關(guān)系”解耦了抽象和實現(xiàn)之間固有的綁定關(guān)系,使得抽象和實現(xiàn)可以沿著各自的維度變化(子類)。
- Bridge模式有時候類似于多繼承,但是多繼承方案往往未被單一職責(zé)原則,復(fù)用性較差。
- Bridge模式的應(yīng)用一般在“兩個非常強(qiáng)的變化維度”,當(dāng)多于兩個的變化維度,可以使用Bridge的擴(kuò)展模式。