為什么要學設計模式?
面試經(jīng)常被問到
以前總覺得設計模式是“花拳繡腿”,實際開發(fā)中沒什么卵用,其實有好多種設計模式自己在無形中就使用了,只是自己不知道
設計模式是軟件開發(fā)人員的“標準詞匯”,學習設計模式是個人技術能力提高的捷徑
設計模式包含了面向?qū)ο蟮木瑁岸嗽O計模式,你就懂了面向?qū)ο蠓治龊驮O計(OOA/D)的精要”
點贊+收藏 就學會系列,文章收錄在 GitHub JavaEgg ,N線互聯(lián)網(wǎng)開發(fā)必備技能兵器譜
軟件設計模式概述
軟件設計模式的產(chǎn)生背景
“設計模式”這個術語最初并不是出現(xiàn)在軟件設計中,而是被用于建筑領域的設計中。
1987 年,肯特·貝克(Kent Beck)和沃德·坎寧安(Ward Cunningham)首先將建筑領域的模式思想應用在 Smalltalk 中的圖形用戶接口的生成中,但沒有引起軟件界的關注。
1994 年,艾瑞克·伽馬(ErichGamma)、理査德·海爾姆(Richard Helm)、拉爾夫·約翰森(Ralph Johnson)、約翰·威利斯迪斯(John Vlissides)等 4 位作者合作出版了《設計模式:可復用面向?qū)ο筌浖幕A》(Design Patterns: Elements of Reusable Object-Oriented Software)一書,在本教程中收錄了 23 個設計模式,這是設計模式領域里程碑的事件,導致了軟件設計模式的突破。這 4 位作者在軟件開發(fā)領域里也以他們的“四人組”(Gang of Four,GoF)匿名著稱。
軟件設計模式的概念與意義
1. 軟件設計模式的概念
軟件設計模式(Software Design Pattern),又稱設計模式,是指在軟件開發(fā)中,經(jīng)過驗證的,用于解決在特定環(huán)境下、重復出現(xiàn)的、特定問題的解決方案。
2. 學習設計模式的意義
設計模式的本質(zhì)是面向?qū)ο笤O計原則的實際運用,是對類的封裝性、繼承性和多態(tài)性以及類的關聯(lián)關系和組合關系的充分理解。正確使用設計模式具有以下優(yōu)點。
- 可以提高程序員的思維能力、編程能力和設計能力。
- 使程序設計更加標準化、代碼編制更加工程化,使軟件開發(fā)效率大大提高,從而縮短軟件的開發(fā)周期。
- 使設計的代碼可重用性高、可讀性強、可靠性高、靈活性好、可維護性強。
軟件設計模式的基本要素
軟件設計模式使人們可以更加簡單方便地復用成功的設計和體系結構,它通常包含以下幾個基本要素:模式名稱、別名、動機、問題、解決方案、效果、結構、模式角色、合作關系、實現(xiàn)方法、適用性、已知應用、例程、模式擴展和相關模式等,其中最關鍵的元素包括以下 4 個主要部分。
1. 模式名稱
每一個模式都有自己的名字,通常用一兩個詞來描述,可以根據(jù)模式的問題、特點、解決方案、功能和效果來命名。
2. 問題
問題(Problem)描述了該模式的應用環(huán)境,即何時使用該模式。它解釋了設計問題和問題存在的前因后果,以及必須滿足的一系列先決條件。
3. 解決方案
模式問題的解決方案(Solution)包括設計的組成成分、它們之間的相互關系及各自的職責和協(xié)作方式。因為模式就像一個模板,可應用于多種不同場合,所以解決方案并不描述一個特定而具體的設計或?qū)崿F(xiàn),而是提供設計問題的抽象描述和怎樣用一個具有一般意義的元素組合(類或?qū)ο蟮慕M合)來解決這個問題。
4. 效果
描述了模式的應用效果以及使用該模式應該權衡的問題,即模式的優(yōu)缺點。主要是對時間和空間的衡量,以及該模式對系統(tǒng)的靈活性、擴充性、可移植性的影響,也考慮其實現(xiàn)問題。
GoF 的 23 種設計模式的分類和功能
1. 根據(jù)目的來分
根據(jù)模式是用來完成什么工作來劃分,這種方式可分為創(chuàng)建型模式結構型模行為型模式
- 創(chuàng)建型模式:用于描述“怎樣創(chuàng)建對象”,它的主要特點是“將對象的創(chuàng)建與使用分離”。GoF 中提供了單例、原型、工廠方法、抽象工廠、建造者等 5 種創(chuàng)建型模式。
- 結構型模式:用于描述如何將類或?qū)ο蟀茨撤N布局組成更大的結構,GoF 中提供了代理、適配器、橋接、裝飾、外觀、享元、組合等 7 種結構型模式。
- 行為型模式:用于描述類或?qū)ο笾g怎樣相互協(xié)作共同完成單個對象都無法單獨完成的任務,以及怎樣分配職責。GoF 中提供了模板方法、策略、命令、職責鏈、狀態(tài)、觀察者、中介者、迭代器、訪問者、備忘錄、解釋器等 11 種行為型模式。
2. 根據(jù)作用范圍來分
根據(jù)模式是主要用于類上還是主要用于對象上來分,這種方式可分為類模式和對象模式
- 類模式:用于處理類與子類之間的關系,這些關系通過繼承來建立,是靜態(tài)的,在編譯時刻便確定下來了。GoF中的工廠方法、(類)適配器、模板方法、解釋器屬于該模式。
- 對象模式:用于處理對象之間的關系,這些關系可以通過組合或聚合來實現(xiàn),在運行時刻是可以變化的,更具動態(tài)性。GoF 中除了以上 4 種,其他的都是對象模式。
23種設計模式的功能
- 單例(Singleton)模式:某個類只能生成一個實例,該類提供了一個全局訪問點供外部獲取該實例,其拓展是有限多例模式。
- 原型(Prototype)模式:將一個對象作為原型,通過對其進行復制而克隆出多個和原型類似的新實例。
- 工廠方法(Factory Method)模式:定義一個用于創(chuàng)建產(chǎn)品的接口,由子類決定生產(chǎn)什么產(chǎn)品。
- 抽象工廠(AbstractFactory)模式:提供一個創(chuàng)建產(chǎn)品族的接口,其每個子類可以生產(chǎn)一系列相關的產(chǎn)品。
- 建造者(Builder)模式:將一個復雜對象分解成多個相對簡單的部分,然后根據(jù)不同需要分別創(chuàng)建它們,最后構建成該復雜對象。
- 代理(Proxy)模式:為某對象提供一種代理以控制對該對象的訪問。即客戶端通過代理間接地訪問該對象,從而限制、增強或修改該對象的一些特性。
- 適配器(Adapter)模式:將一個類的接口轉換成客戶希望的另外一個接口,使得原本由于接口不兼容而不能一起工作的那些類能一起工作。
- 橋接(Bridge)模式:將抽象與實現(xiàn)分離,使它們可以獨立變化。它是用組合關系代替繼承關系來實現(xiàn),從而降低了抽象和實現(xiàn)這兩個可變維度的耦合度。
- 裝飾(Decorator)模式:動態(tài)的給對象增加一些職責,即增加其額外的功能。
- 外觀(Facade)模式:為多個復雜的子系統(tǒng)提供一個一致的接口,使這些子系統(tǒng)更加容易被訪問。
- 享元(Flyweight)模式:運用共享技術來有效地支持大量細粒度對象的復用。
- 組合(Composite)模式:將對象組合成樹狀層次結構,使用戶對單個對象和組合對象具有一致的訪問性。
- 模板方法(TemplateMethod)模式:定義一個操作中的算法骨架,而將算法的一些步驟延遲到子類中,使得子類可以不改變該算法結構的情況下重定義該算法的某些特定步驟。
- 策略(Strategy)模式:定義了一系列算法,并將每個算法封裝起來,使它們可以相互替換,且算法的改變不會影響使用算法的客戶。
- 命令(Command)模式:將一個請求封裝為一個對象,使發(fā)出請求的責任和執(zhí)行請求的責任分割開。
- 職責鏈(Chain of Responsibility)模式:把請求從鏈中的一個對象傳到下一個對象,直到請求被響應為止。通過這種方式去除對象之間的耦合。
- 狀態(tài)(State)模式:允許一個對象在其內(nèi)部狀態(tài)發(fā)生改變時改變其行為能力。
- 觀察者(Observer)模式:多個對象間存在一對多關系,當一個對象發(fā)生改變時,把這種改變通知給其他多個對象,從而影響其他對象的行為。
- 中介者(Mediator)模式:定義一個中介對象來簡化原有對象之間的交互關系,降低系統(tǒng)中對象間的耦合度,使原有對象之間不必相互了解。
- 迭代器(Iterator)模式:提供一種方法來順序訪問聚合對象中的一系列數(shù)據(jù),而不暴露聚合對象的內(nèi)部表示。
- 訪問者(Visitor)模式:在不改變集合元素的前提下,為一個集合中的每個元素提供多種訪問方式,即每個元素有多個訪問者對象訪問。
- 備忘錄(Memento)模式:在不破壞封裝性的前提下,獲取并保存一個對象的內(nèi)部狀態(tài),以便以后恢復它。
- 解釋器(Interpreter)模式:提供如何定義語言的文法,以及對語言句子的解釋方法,即解釋器。
設計模式七大原則
設計模式的目的
編寫軟件過程中,程序員面臨著來自耦合性,內(nèi)聚性以及可維護性,可擴展性,重用性,靈活性等多方面的挑戰(zhàn),設計模式是為了讓程序(軟件),具有更好的
- 代碼重用性 (即:相同功能的代碼,不用多次編寫)
- 可讀性 (即:編程規(guī)范性, 便于其他程序員的閱讀和理解)
- 可擴展性 (即:當需要增加新的功能時,非常的方便,稱為可維護)
- 可靠性 (即:當我們增加新的功能后,對原來的功能沒有影響)
- 使程序呈現(xiàn)高內(nèi)聚,低耦合的特性
設計模式七大原則
設計模式原則,其實就是程序員在編程時,應當遵守的原則,也是各種設計模式的基礎(即:設計模式為什么這樣設計的依據(jù))
1. 單一職責原則
單一職責原則表示一個模塊的組成元素之間的功能相關性。從軟件變化的角度來看,對類來說,一個類應該只負責一項職責。
假設某個類 P 負責兩個不同的職責,職責 P1 和 職責 P2,那么當職責 P1 需求發(fā)生改變而需要修改類 P,有可能會導致原來運行正常的職責 P2 功能發(fā)生故障。
單一職責原則注意事項和細節(jié)
- 降低類的復雜度,一個類只負責一項職責
- 提高類的可讀性,可維護性
- 降低變更引起的風險
- 通常情況下,我們應當遵守單一職責原則,只有邏輯足夠簡單,才可以在代碼級違反單一職責原則;只有類中方法數(shù)量足夠少,可以在方法級別保持單一職責原則
2. 接口隔離原則
客戶端不應該依賴它不需要的接口,即一個類對另一個類的依賴應該建立在最小的接口上
接口隔離原則,其 "隔離" 并不是準確的翻譯,真正的意圖是 “分離” 接口(的功能)
3. 開閉原則
開放-關閉原則表示軟件實體 (類、模塊、函數(shù)等等) 應該是可以被擴展的,但是不可被修改。(Open for extension, close for modification)
如果一個軟件能夠滿足 OCP 原則,那么它將有兩項優(yōu)點:
- 能夠擴展已存在的系統(tǒng),能夠提供新的功能滿足新的需求,因此該軟件有著很強的適應性和靈活性。
- 已存在的模塊,特別是那些重要的抽象模塊,不需要被修改,那么該軟件就有很強的穩(wěn)定性和持久性。
4. 依賴倒轉(倒置)原則
依賴倒轉原則(Dependence Inversion Principle)是指:
- 高層模塊不應該依賴低層模塊,二者都應該依賴其抽象
- 抽象不應該依賴細節(jié),細節(jié)應該依賴抽象
- 依賴倒轉(倒置)的中心思想是面向接口編程
- 依賴倒轉原則是基于這樣的設計理念:相對于細節(jié)的多變性,抽象的東西要穩(wěn)定的多。以抽象為基礎搭建的架構比以細節(jié)為基礎的架構要穩(wěn)定的多。在 java 中,抽象指的是接口或抽象類,細節(jié)就是具體的實現(xiàn)類
- 使用接口或抽象類的目的是制定好規(guī)范,而不涉及任何具體的操作,把展現(xiàn)細節(jié)的任務交給他們的實現(xiàn)類去完成
5. 里氏替換原則
在編程中常常會遇到這樣的問題:有一功能 P1, 由類 A 完成,現(xiàn)需要將功能 P1 進行擴展,擴展后的功能為 P,其中P由原有功能P1與新功能P2組成。新功能P由類A的子類B來完成,則子類B在完成新功能P2的同時,有可能會導致原有功能P1發(fā)生故障。
里氏替換原則告訴我們,當使用繼承時候,類 B 繼承類 A 時,除添加新的方法完成新增功能 P2,盡量不要修改父類方法預期的行為。
里氏替換原則的重點在不影響原功能,而不是不覆蓋原方法。
6. 迪米特法則
- 一個對象應該對其他對象保持最少的了解
- 類與類關系越密切,耦合度越大
- 迪米特法則(Demeter Principle)又叫最少知道原則,即一個類對自己依賴的類知道的越少越好。也就是說,對于被依賴的類不管多么復雜,都盡量將邏輯封裝在類的內(nèi)部。對外除了提供的 public 方法,不對外泄露任何信息
- 迪米特法則還有個更簡單的定義:只與直接的朋友通信
- 直接的朋友:每個對象都會與其他對象有耦合關系,只要兩個對象之間有耦合關系,我們就說這兩個對象之間是朋友關系。耦合的方式很多,依賴,關聯(lián),組合,聚合等。其中,我們稱出現(xiàn)成員變量,方法參數(shù),方法返回值中的類為直接的朋友,而出現(xiàn)在局部變量中的類不是直接的朋友。也就是說,陌生的類最好不要以局部變量的形式出現(xiàn)在類的內(nèi)部。
7. 合成復用原則
組合/聚合復用原則就是在一個新的對象里面使用一些已有的對象,使之成為新對象的一部分; 新的對象通過向這些對象的委派達到復用已有功能的目的。
在面向?qū)ο蟮脑O計中,如果直接繼承基類,會破壞封裝,因為繼承將基類的實現(xiàn)細節(jié)暴露給子類;如果基類的實現(xiàn)發(fā)生了改變,則子類的實現(xiàn)也不得不改變;從基類繼承而來的實現(xiàn)是靜態(tài)的,不可能在運行時發(fā)生改變,沒有足夠的靈活性。于是就提出了組合/聚合復用原則,也就是在實際開發(fā)設計中,盡量使用組合/聚合,不要使用類繼承
學設計模式還得用大二學過的UML類圖,所以重新預習下。
UML
UML(Unified Modeling Language)是一種統(tǒng)一建模語言,為面向?qū)ο箝_發(fā)系統(tǒng)的產(chǎn)品進行說明、可視化、和編制文檔的一種標準語言
基于UML1.5 的UML種類
類圖
類圖(ClassDiagram)是用來顯示系統(tǒng)中的類、接口、協(xié)作以及它們之間的靜態(tài)結構和關系的一種靜態(tài)模型,是我們 Java 猿帥們最需要掌握的。它主要用于描述軟件系統(tǒng)的結構化設計,幫助人們簡化對軟件系統(tǒng)的理解,它是系統(tǒng)分析與設計階段的重要產(chǎn)物,也是系統(tǒng)編碼與測試的重要模型依據(jù)。
類圖中的類可以通過某種編程語言直接實現(xiàn)。類圖在軟件系統(tǒng)開發(fā)的整個生命周期都是有效的,它是面向?qū)ο笙到y(tǒng)的建模中最常見的圖。
類圖表示法
類之間的關系
在軟件系統(tǒng)中,類不是孤立存在的,類與類之間存在各種關系。根據(jù)類與類之間的耦合度從弱到強排列,UML 中的類圖有以下幾種關系:依賴關系、關聯(lián)關系、聚合關系、組合關系、泛化關系和實現(xiàn)關系。其中泛化和實現(xiàn)的耦合度相等,它們是最強的。
1. 依賴關系
只要在類中用到了對方,那么他們之間存在依賴關系,依賴(Dependency)關系是一種使用關系,它是對象之間耦合度最弱的一種關聯(lián)方式,是臨時性的關聯(lián)。在代碼中,某個類的方法通過局部變量、方法的參數(shù)或者對靜態(tài)方法的調(diào)用來訪問另一個類(被依賴類)中的某些方法來完成一些職責。
在 UML 類圖中,依賴關系使用帶箭頭的虛線來表示,箭頭從使用類指向被依賴的類
2. 關聯(lián)關系
關聯(lián)(Association)關系是對象之間的一種引用關系,用于表示一類對象與另一類對象之間的聯(lián)系,如老師和學生、師傅和徒弟、丈夫和妻子等。關聯(lián)關系是類與類之間最常用的一種關系,分為一般關聯(lián)關系、聚合關系和組合關系。
關聯(lián)具有導航性,即雙向關系和單向關系。
關聯(lián)具有多重性,如“1”表示有且僅有一個,“0..*”表示0個或多個。
單向關聯(lián)
雙向關聯(lián)
多重關聯(lián)
3. 聚合關系
聚合(Aggregation)關系表示的是整體和部分的關系,是 has-a 的關系,整體與部分可以分開,是關聯(lián)關系的特例,是強關聯(lián)關系。
聚合關系也是通過成員對象來實現(xiàn)的,其中成員對象是整體對象的一部分,但是成員對象可以脫離整體對象而獨立存在。例如,學校與老師的關系,學校包含老師,但如果學校停辦了,老師依然存在。
4.組合關系
組合(Composition)關系也是關聯(lián)關系的一種特例,也表示類之間的整體與部分的關系,但它是一種更強烈的聚合關系,不可以分開,是 cxmtains-a 關系。
在組合關系中,整體對象可以控制部分對象的生命周期,一旦整體對象不存在,部分對象也將不存在,部分對象不能脫離整體對象而存在。例如,公司和部門。
5.泛化關系
泛化(Generalization)關系是對象之間耦合度最大的一種關系,表示一般與特殊的關系,是父類與子類之間的關系,是一種繼承關系,是 is-a 的關系。
在 UML 類圖中,泛化關系用帶空心三角箭頭的實線來表示,箭頭從子類指向父類。在代碼實現(xiàn)時,使用面向?qū)ο蟮睦^承機制來實現(xiàn)泛化關系。
6.實現(xiàn)關系
實現(xiàn)(Realization)關系是接口與實現(xiàn)類之間的關系。在這種關系中,類實現(xiàn)了接口,類中的操作實現(xiàn)了接口中所聲明的所有的抽象操作。
在 UML 類圖中,實現(xiàn)關系使用帶空心三角箭頭的虛線來表示,箭頭從實現(xiàn)類指向接口。
參考
https://zhuanlan.zhihu.com/p/44518805