What
設計模式(Design Pattern)是前人針對面向對象設計中反復出現的問題,總結出的設計套路。這個術語是在1990年代由Erich Gamma等人從建筑設計領域引入到計算機科學中來的。
算法不是DP,算法致力于解決本質問題,而DP更側重布局設計問題;DP不能讓算法變得更準確或者更高效,但可能會讓算法實現的更優雅(好吧,更可復用、好維護、易擴展、更可靠等)。
Why
正確使用設計模式具有以下優點。
- 提高思維和設計能力,進而影響編程能力
- 使程序設計更加標準化、代碼編制更加工程化,進而大大提高軟件開發效率
- 使設計的代碼可重用性高、可讀性強、可靠性高、靈活性好、可維護性強。
6大設計原則(對封裝、繼承、多態的進一步原則指導)
- 開閉原則:軟件實體應當對擴展開放,對修改關閉(避免影響已有的邏輯)
- 單一職責原則:類的職責盡量專注單一,從而達到內聚松耦合
- 里氏替換原則:只要父類出現的地方子類就可以出現,且替換成子類也不會出現任何錯誤或者異常(沒事別瞎繼承!!)
- 依賴倒置原則:面向接口編程,而非面向實現編程(高層模塊不應該依賴低層模塊,兩者都應該依賴其抽象;抽象不應該依賴細節,細節應該依賴抽象)
- 接口隔離原則:不要建立臃腫龐大的接口。即接口盡量細化,客戶端不應該被迫依賴于它不使用的方法
- 迪米特法則: 一個對象應該對其他對象(接口、參數、返回值等)有最少的了解(為了解耦、安全等)
開閉原則
軟件實體應當對擴展開放,對修改關閉(避免影響已有的邏輯)
軟件實體包括以下幾個部分:
- 模塊
- 類與接口
- 方法
實現方法:抽象約束、封裝變化
即通過接口或者抽象類為軟件實體定義一個相對穩定的抽象層,而將相同的可變因素封裝在相同的具體實現類中。
因為抽象靈活性好,適應性廣,只要抽象的合理,可以基本保持軟件架構的穩定。
而軟件中易變的細節可以從抽象派生來的實現類來進行擴展,當軟件需要發生變化時,只需要根據需求重新派生一個實現類來擴展就可以了。
舉例:Windows 的桌面主題設計
單一職責原則
類的職責盡量專注單一,從而達到高內聚低耦合
該原則提出對象不應該承擔太多職責,如果一個對象承擔了太多的職責,至少存在以下兩個缺點:
- 一個職責的變化可能會削弱或者抑制這個類實現其他職責的能力;
- 當客戶端需要該對象的某一個職責時,不得不將其他不需要的職責全都包含進來,從而造成冗余代碼或代碼的浪費。
實現方式:找準核心實體,發現多重職責,進行分離
單一職責原則是最簡單但又最難運用的原則,需要設計人員發現類的不同職責并將其分離,再封裝到不同的類或模塊中。
而發現類的多重職責需要設計人員具有較強的分析設計能力和相關重構經驗。
舉例 :學生工作
分析:大學學生工作主要包括學生生活輔導和學生學業指導兩個方面的工作,其中生活輔導主要包括班委建設、出勤統計、心理輔導、費用催繳、班級管理等工作,學業指導主要包括專業引導、學習輔導、科研指導、學習總結等工作。如果將這些工作交給一位老師負責顯然不合理,正確的做 法是生活輔導由輔導員負責,學業指導由學業導師負責
里氏替換原則
只要父類出現的地方子類就可以出現,且替換成子類也不會出現任何錯誤或者異常(沒事別瞎繼承!!)
子類可以擴展父類的功能,但不能改變父類原有的功能。
實現方式:取消原來的繼承關系,重新設計它們之間的關系
舉例:幾維鳥不是鳥”
不合理的繼承關系:
較合理的繼承關系:
依據:
鳥一般都會飛行,如燕子的飛行速度大概是每小時 120 千米。但是新西蘭的幾維鳥由于翅膀退化無法飛行。假如要設計一個實例,計算這兩種鳥飛行 300 千米要花費的時間。顯然,拿燕子來測試這段代碼,結果正確,能計算出所需要的時間;但拿幾維鳥來測試,結果會發生“除零異常”或是“無窮大”,明顯不符合預期
正確的做法是:取消幾維鳥原來的繼承關系,定義鳥和幾維鳥的更一般的父類,如動物類,它們都有奔跑的能力。幾維鳥的飛行速度雖然為 0,但奔跑速度不為 0,可以計算出其奔跑 300 千米所要花費的時間。
依賴倒置原則
面向接口編程,而非面向實現編程(高層模塊不應該依賴低層模塊,兩者都應該依賴其抽象;抽象不應該依賴細節,細節應該依賴抽象);這里的抽象指的是接口或者抽象類,而細節是指具體的實現類。
實現
- 每個類盡量提供接口或抽象類,或者兩者都具備。
- 變量的聲明類型盡量是接口或者是抽象類。
- 任何類都不應該從具體類派生。
- 使用繼承時盡量遵循里氏替換原則。
舉例:顧客購物程序
接口隔離原則
不要建立臃腫龐大的接口。即接口盡量細化,客戶端不應該被迫依賴于它不使用的方法;
實現
- 接口的粒度大小定義合理
- 不宜定義過小,否則會造成接口數量過多,使設計復雜化;
- 使用多個專門的接口還能夠體現對象的層次,因為可以通過接口的繼承,實現對總接口的定義。
舉例:學生成績管理程序
學生成績管理程序一般包含插入成績、刪除成績、修改成績、計算總分、計算均分、打印成績信息、査詢成績信息等功能,如果將這些功能全部放到一個接口中顯然不太合理,正確的做法是將它們分別放在輸入模塊、統計模塊和打印模塊等 3 個模塊中。
迪米特法則
一個對象應該對其他對象(接口、參數、返回值等)有最少的了解(為了解耦、安全等)
但是,過度使用迪米特法則會使系統產生大量的中介類,從而增加系統的復雜性,使模塊之間的通信效率降低。
實現
從依賴者的角度來說,只依賴應該依賴的對象。
從被依賴者的角度說,只暴露應該暴露的方法。
舉例:明星與經紀人的關系實例
明星由于全身心投入藝術,所以許多日常事務由經紀人負責處理,如與粉絲的見面會,與媒體公司的業務洽淡等。這里的經紀人是明星的朋友,而粉絲和媒體公司是陌生人,所以適合使用迪米特法則
組成聚合原則
它要求在軟件復用時,要盡量先使用組合或者聚合等關聯關系來實現,其次才考慮使用繼承關系來實現。
實現
合成復用原則是通過將已有的對象納入新對象中,作為新對象的成員對象來實現的,新對象可以調用已有對象的功能,從而達到復用。
舉例:汽車分類管理
汽車按“動力源”劃分可分為汽油汽車、電動汽車等;按“顏色”劃分可分為白色汽車、黑色汽車和紅色汽車等。如果同時考慮這兩種分類,其組合就很多。
不好的設計
推薦的設計