大話設計模式、UML、設計模式Java版完全總結
此篇部落格為閱讀大話設計模式後的筆記記錄,注意是筆記形式,優先適合於對設計模式有一定了解的讀者,希望短時間快速溫習的讀者,同時也對所有設計模式添加了完整程式碼詮釋與註釋,方便初學者的理解,另外,文章末尾有對所有設計模式的總結,讀者若對部分設計模式容易混淆,可以到文章末尾進行了解,其中文章有些內容我覺得不方便展開的,會附上我認為的比較優秀的部落格地址進行講解。
轉載請註明出處哦~https://blog.csdn.net/qq_35642036/article/details/79663378
設計模式分為三大類:
建立型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。
結構型模式,共七種:介面卡模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。
行為型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代器模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、直譯器模式。
其實還有兩類:併發型模式和執行緒池模式。
UML講解
詳細參考網址:http://blog.csdn.net/garfielder007/article/details/54427742
UML即Unified Model Language,是一種建模語言,也是標準建模語言。
常見的有以下幾種關係: 泛化(Generalization), 實現(Realization), 關聯(Association), 聚合(Aggregation), 組合(Composition),依賴(Dependency)
1. 泛化(繼承非抽象類):帶空心三角形的直線表示
2. 實現(繼承抽象類,類實現介面):帶空心三角形的虛線表示
3. 依賴:類與類之間最弱的關係,依賴可以簡單的理解一個類使用了另一個類:帶箭頭的虛線表示依賴
例如 :Person類使用了car類裡面的speed()(一般在方法引數)
4. 關聯:一個類和另一類有聯絡:帶箭頭的實線表示
例如:Person類裡面有Address類屬性(一般在成員變數)
關聯關係是一種包含關係,在UML中用一個帶箭頭的實線表示,箭頭指向被包含類。在UML類中有如下幾種。
1..1:表示另一個類的一個物件只與該類的一個物件有關係
0..*:表示另一個類的一個物件與該類的零個或多個物件有關係
1..*:表示另一個類的一個物件與該類的一個或多個物件有關係
0..1:表示另一個類的一個物件沒有或只與該類的一個物件有關係
* :任意多個物件關聯
5. 聚合:表示整體與部分的關係,但是部分可以脫離整體而存在:帶空心菱形的直線加箭頭表示,has-a關係
Person類裡面有car類屬性
6. 組合:部分和整體的關係,但是部分存活週期受到整體的影響,若整體不存在則部分也將不存在。此時部分需在整體的構造方法中建立:帶實心菱形的直線加箭頭表示。Contains-a關係
person類裡面有finger類屬性
關係所表現的強弱程度依次為:組合>聚合>關聯>依賴;
聚合跟組合其實都屬於關聯 只不過它們是兩種特殊的關聯
設計模式原則
總原則-開閉原則OCP
對擴充套件開放,對修改封閉。在程式需要進行拓展的時候,不能去修改原有的程式碼,而是要擴充套件原有程式碼,實現一個熱插拔的效果。所以一句話概括就是:為了使程式的擴充套件性好,易於維護和升級。
設計模式的六大原則:
1、單一職責原則
不要存在多於一個導致類變更的原因,也就是說每個類應該實現單一的職責,否則就應該把類拆分。
2、里氏替換原則(Liskov Substitution Principle)
任何基類可以出現的地方,子類一定可以出現。
3、依賴倒置原則(Dependence Inversion Principle)
面向介面程式設計,依賴於抽象而不依賴於具體。寫程式碼時用到具體類時,不與具體類互動,而與具體類的上層介面互動。
4、介面隔離原則(Interface Segregation Principle)
每個介面中不存在子類用不到卻必須實現的方法,如果不然,就要將介面拆分。使用多個隔離的
5、迪米特法則(最少知道原則)(Demeter Principle)
一個類對自己依賴的類知道的越少越好。無論被依賴的類多麼複雜,都應該將邏輯封裝在方法的內部,通過public方法提供給外部。這樣當被依賴的類變化時,才能最小的影響該類。介面,比使用單個介面(多個介面方法集合到一個的介面)要好。
6、合成複用原則(Composite Reuse Principle)
儘量首先使用合成/聚合的方式,而不是使用繼承。
設計模式
策略模式(Strategy)
多個演算法可實現類似功能,若將所有方法寫在一個sortUtils裡面會導致難以維護,程式碼複雜。所以策略模式考慮如何讓演算法和物件分開來,使得演算法可以獨立於使用它的客戶而變化。具體的方案是把一個類中經常改變或者將來可能改變的部分提取出來,作為一個介面,然後在類中包含這個物件的例項,這樣類的例項在執行時就可以隨意呼叫實現了這個介面的類的行為。 策略模式就是用來封裝演算法的,但在實踐中,我們發現可以用它來封裝幾乎任何型別的規則,只要在分析過程中聽到需要在不同時間應用不同的業務規則,就可以考慮使用策略模式處理這種變化的可能性。 優點 1、可以動態的改變物件的行為 缺點 1、客戶端必須知道所有的策略類,並自行決定使用哪一個策略類 2、策略模式將造成產生很多策略類 輸出: |
簡單工廠模式(Simple Factory)+工廠方法(FactoryMethod)
工廠模式可以分為三類: 1)簡單工廠模式(Simple Factory):不符合開放-封閉原則 2)工廠方法(Factory Method):生產單一產品 3)抽象工廠模式(Abstract Factory):生產一個產品體系 簡單工廠模式有一個具體的工廠類 工廠方法模式只有一個抽象產品類,而抽象工廠模式有多個。 工廠方法模式的具體工廠類只能建立一個具體產品類的例項,而抽象工廠模式可以建立多個。 簡單工廠模式:一個上帝類,能夠生產A車,若有一種B車需要生產,則需要更改上帝類工廠程式碼
工廠方法模式:去掉了簡單工廠模式中工廠方法的靜態屬性,使得它可以被子類繼承。由於簡單工廠模式不僅對擴充套件開放了,也對修改開放了(每新增一個類,就得去生成例項的工廠方法中增加新的分支),違背了“開放-封閉原則”。工廠方法把簡單工廠的內部邏輯判斷轉移到了客戶端程式碼來進行。缺點:每加一個產品,需要額外加一個產品工廠的類,增加了額外的開銷。
工廠模式用於處理 如何獲取例項物件問題,建造者模式用於處理如何建造例項物件 問題。 工廠方法的實現並不能減少工作量,但是它能夠在必須處理新情況時,避免使已經很複雜的程式碼更加複雜 通常設計應該是從工廠方法開始,當設計者發現需要更大的靈活性時,設計便會向其他建立型模式演化。當設計者在設計標準之間進行權衡的時候,瞭解多個建立型模式可以給設計者更多的選擇餘地 |
裝飾模式(Decorator)
動態給一個物件新增一些額外的職責,使用Decorator模式相比用生成子類方式達到功能的擴充顯得更為靈活。 設計初衷:通常可以使用繼承來實現功能的拓展,如果這些需要拓展的功能的種類很繁多,那麼勢必生成很多子類,增加系統的複雜性,同時,使用繼承實現功能拓展,我們必須可預見這些拓展功能,這些功能是編譯時就確定了,是靜態的。 裝飾者模式: 裝飾者模式必然有一個公共的介面或抽象類,用來作為物件的傳遞。你需要根據介面實現基本的被裝飾類(Person),以及裝飾類的公共介面(Decorator ),以後所有的裝飾類都是繼承自公共的裝飾類介面,內部實現。
輸出: 裝飾者模式也可用於給一個方法新增開始時間,結束時間,或者新增事務,提交事務等 核心要點:有一個抽象物件類或物件介面,而具體的物件和裝飾類都要繼承類或實現這個介面 裝飾程式碼二:
|
代理模式(Proxy)
代理模式:為其他物件提供一種代理以控制對這個物件的訪問,使得其他物件無法直接接觸目標物件
輸出:真實物件的請求 |
原型模式(Prototype)
原型模式:用原型例項指定建立物件的種類,並且通過拷貝這些原型建立新的物件。 涉及了淺拷貝與深拷貝 參考學習網址:https://www.cnblogs.com/Qian123/p/5710533.html#_label3 原型模式其實就是從一個物件再建立另外一個可定製的物件,而且不需要知道任何建立的細節 優點:建立相應數目的原型並克隆它們通常比每次用合適的狀態手工例項化該類更方便一些 輸出: |
模板方法模式(Template)
模板方法模式:定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟
輸出: 具體類B的方法1實現 比如 AsyncTask 裡面的四個方法 onPreExecute 、 doInBackground 、 onProgressUpdate 、 onPostExecute 還有 Android裡面的Activity 也應用了模板方法模式 onCreate 、 onStart 、 onResume 、 onPause 、 onStop 、 onDestroy 、 onRestart |
外觀模式(Facade)
為子系統中的一組介面提供一個一致的頁面,此模式定義了一個高層介面,這個介面使得這一子系統更加容易使用(完美體現了依賴倒轉原則和迪米特法則) 案例解釋:一個人買很多股票,由於股票眾多難以管理容易造成虧損,可以選擇購買基金,由基金來管理這些股票(基金擅長管理股票) 輸出: |
建造者模式(Builder)
將一個複雜的物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。 案例解釋:麥當勞的漢堡包永遠不會少加鹽,擺攤的每次炒的東西味道可能有變化 使用建造者模式,使用者只需指定需要建造的型別就可以得到它們,而具體建造的過程和細節就不需知道了。 什麼時候使用建造者模式:需要建立一些複雜的物件,這些物件內部構建間的建造順序通常是穩定的,但物件內部的構建通常面臨著複雜的變化。 建造者模式如何實現高內聚鬆耦合的?將一個複雜物件的建立與它的表示分離,這就可以很容易地改變一個產品的內部表示,並且使得構造程式碼和表示程式碼分開。這樣對於客戶來說,它無需關心產品的建立過程,而只要告訴建造者需要什麼,就能用同樣的構建過程建立不同的產品給客戶
與抽象工廠的區別:在建造者模式裡,有個指導者,由指導者來管理建造者,使用者是與指導者聯絡的,指導者聯絡建造者最後得到產品。即建造模式可以強制實行一種分步驟進行的建造過程。 |
觀察者模式(Observer)
觀察者模式(釋出-訂閱模式):定義了一種一對多的依賴關係,讓多個觀察者物件同時監聽某一個主題物件。這個主題物件在狀態傳送變化時,會通知所有觀察者物件,讓它們能夠自動更新自己 一個被觀察者管理所有相依於它的觀察者物件,並且在本身的狀態改變時主動發出通知。這通常通過呼叫各觀察者所提供的方法來實現。此種模式通常被用來實現事件處理系統。 案例解釋:前臺小姐concreteSubject、裡面的工作的人concreteObserver、老闆來了前臺小姐通知裡面工作的人馬上開始工作 輸出: 觀察者Z的新狀態是ABC 應用:珠寶商運送一批鑽石,有黃金強盜準備搶劫,珠寶商僱傭了私人保鏢,警察局也派人護送,於是當運輸車上路的時候,強盜保鏢警察都要觀察運輸車一舉一動 程式: 1. 抽象的被觀察者watched介面,有addWatcher,deleteWatch,notifyWatch方法 2. 抽象的觀察者watcher介面,update方法 3. 具體的被觀察者Transporter實現watched介面 4. 三個具體的觀察者實現watcher,有具體的update方法 5. 測試類 觀察者模式在關於目標角色、觀察者角色通訊的具體實現中,有兩個版本。 一種情況便是目標角色在發生變化後,僅僅告訴觀察者角色“我變化了”,觀察者角色如果想要知道具體的變化細節,則就要自己從目標角色的介面中得到。這種模式被很形象的稱為:拉模式——就是說變化的資訊是觀察者角色主動從目標角色中“拉”出來的。 還有一種方法,那就是我目標角色“服務一條龍”,通知你發生變化的同時,通過一個引數將變化的細節傳遞到觀察者角色中去。這就是“推模式”——管你要不要,先給你啦。 |
抽象工廠模式(AbstractFactory)
提供一個建立一系列相關或相互依賴物件的介面,而無需指定它們具體的類 好處: 1. 易於交換產品系列,例如Ifactory factory = new AccessFactory()在一個應用中只需要在初始化的時候出現一次,這就使得改變一個應用的具體工廠變得非常容易,它只需要改變具體工廠即可使用不同的產品配置。 2. 它讓具體的建立例項過程與客戶端分離,客戶端是通過它們的抽象介面操縱例項,產品的具體類名也被具體工廠的實現分離,不會出現在客戶程式碼中。 所有在用簡單工廠的地方,都可以考慮用反射技術來去除switch或if,解除分支判斷帶來的耦合 缺點:如果要新增一個產品類,就得增加很多程式碼 用反射+抽象工廠的資料訪問程式 public class OperationFactory {
|
狀態模式(State)
當一個物件的內在狀態改變時允許改變其行為,這個物件看起來像是改變了其類 狀態模式主要解決的是當控制一個物件狀態轉換的條件表示式過於複雜時的情況。把狀態的判斷邏輯轉移到表示不同狀態的一系列類當中,可以把複雜的判斷邏輯簡化。 好處:將與特定狀態相關的行為區域性化,並且將不同狀態的行為分割開來。 也就是將特定的狀態相關的行為都放入一個物件中,由於所有與狀態相關的程式碼都存在於某個ConcreteState中,所以通過定義性的子類可以很容易地增加新的狀態和轉換。 目的:消除龐大的條件分支語句;狀態模式通過把各種狀態轉移邏輯分佈到State的子類之間,來減少相互間的依賴。 什麼時候用: 當一個物件的行為取決於它的狀態,並且它必須在執行時刻根據狀態改變它的行為時,就可以考慮使用狀態模式。 //Context類,維護一個ConcreteState子類的例項,這個例項定義當前的狀態 public class Context { //抽象狀態類 //具體狀態類A //客戶端:不斷請求,不斷更改狀態 |
介面卡模式(Adapter)
將一個類的介面轉換成客戶希望的另外一個介面。Adapter模式使得原本由於介面不相容而不能一起工作的那些類可以在一起工作。 優點 通過介面卡,客戶端可以呼叫同一介面,因而對客戶端來說是透明的。這樣做更簡單、更直接、更緊湊。 複用了現存的類,解決了現存類和複用環境要求不一致的問題。 將目標類和適配者類解耦,通過引入一個介面卡類重用現有的適配者類,而無需修改原有程式碼。 一個物件介面卡可以把多個不同的適配者類適配到同一個目標,也就是說,同一個介面卡可以把適配者類和它的子類都適配到目標介面。 缺點 對於物件介面卡來說,更換介面卡的實現過程比較複雜。 適用場景 系統需要使用現有的類,而這些類的介面不符合系統的介面。 想要建立一個可以重用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起工作。 兩個類所做的事情相同或相似,但是具有不同介面的時候。 舊的系統開發的類已經實現了一些功能,但是客戶端卻只能以另外介面的形式訪問,但我們不希望手動更改原有類的時候。 使用第三方元件,元件介面定義和自己定義的不同,不希望修改自己的介面,但是要使用第三方元件介面的功能。 實現: public abstract class Target { //需要適配的類 //介面卡類,通過在內部包裝一個Adaptee物件,把原介面轉換成目標介面 //介面卡客戶端 Android 裡的 ListView 和 RecyclerView 的 setAdapter() 方法就是使用了介面卡模式。 |
備忘錄模式(Memento)
在不破壞封裝性的前提下,捕獲一個物件的內部狀態,並在該物件之外儲存這個狀態,這樣以後就可將該物件回覆到原先儲存的狀態 Memento比較適合用於功能複雜的,但需要維護或記錄屬性歷史的類,或者需要儲存的屬性只是眾多屬性中的一小部分時,Originator可以根據儲存的Memento資訊還原到前一狀態。 如果在某個系統中使用命令模式時,需要實現命令的撤銷功能,那麼命令模式可以使用備忘錄模式來儲存可撤銷操作的狀態。 使用備忘錄可以把複雜的物件內部資訊對其他的物件遮蔽起來,從而可以恰當地保持封裝的邊界
|
組合模式(Componet)
將物件組合成樹形結構以表示“部分-整體”的層次結構。組合模式使得用於對單個物件和組合物件的使用具有一致性 何時使用? 答:需求中是體現部分與整體層次的結構時,以及希望使用者可以忽略組合物件與單個物件的不同,統一地使用組合結構中的所有物件時,就應該考慮用組合模式 //Component為組合中的物件宣告介面,在適當情況下,實現所有類共有介面的預設行為。 //Leaf在組合中表示葉節點物件,葉節點沒有子節點,實際上它的add、remove方法都是多餘的,實際上我覺得這裡有點違反了介面隔離原則:每個介面中不存在子類用不到卻必須實現的方法,也就是Component介面存在子類Leaf用不上卻必須實現的方法 //定義有枝節點行為,用來儲存子部件 //客戶端。通過Component介面操作組合部件的物件 打印出: 程式中提到Leaf實現類實現了add與remove方法,而這兩個方法對其本身並沒有用處,這種方式叫透明方式,也就是說在Component中宣告所有用來管理子物件的方法,其中包括add、remove等,這樣實現Component介面的所有子類都具備了Add和remove,這樣做的好處就是葉節點和枝節點對於外界沒有區別,它們具備完全一致的行為介面。但問題也很明顯,因為Leaf類本身不具備Add、remove方法的功能,所以實現它是沒有意義的。 |
迭代器模式(Iterator)
提供一種方法順序訪問一個聚合物件中各個元素,而又不暴露該物件的內部表示。 Java等語言都已經對其進行了封裝 程式介紹:用一個Aggregate裡面儲存一個list,帶有控制list的方法,而Iterator裡面包裝這個Aggregate,將對Iterator的各個介面方法的操作重寫為對Aggregate的操作。Aggregate裡面有一個可以建立其對應的Iterator的方法 //聚集介面,可以理解為Iterable //具體聚集類 //迭代器介面 //具體迭代器類,給出一種具體迭代的實現方式。思考:迭代器表示的是一種迭代的行為,而聚集則是真正要被迭代的資料集合。之所以要將迭代器和聚集分開,就是為了將行為與資料分開。 可類比Java中Iterator與Iterable的關係進行理解 this.setConcreteAggregate(concreteAggregate); } return current >= concreteAggregate.count() ? true : false; } this.concreteAggregate = concreteAggregate; } //迭代器客戶端 |
單例模式(Singleton)
保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。 優點:讓類自身負責儲存它的唯一例項。這個類可以保證沒有其他例項可以被建立,並且還提供了一個訪問該例項的方法,這樣就使得對唯一的例項可以嚴格地控制客戶怎樣以及何時訪問它。 懶漢式、餓漢式、synchronized懶或餓漢式、雙檢鎖DCL、volatile方式(工作記憶體優化方式)、靜態內部類方式、列舉類方式
如果單例模式不是用列舉類建立的,那麼其無法防止其序列化過程破壞單例特性,因為在序列化過程會利用反射呼叫單例的私有構造方法生成新的物件,如果要避免這種破壞,就要在單例類裡面新增方法readResolve(), 具體參考:http://mp.weixin.qq.com/s/iXC47w4tMfpZzTNxS_JQOw 1. 有一個雙重檢驗鎖的單例模式(即版本五或版本六) 2. 序列化測試程式碼如下 |
橋接模式(Bridge)
將抽象部分與它的實現部分分離,使它們都可以獨立地變化。 這裡並不是說讓抽象類與其派生類分離,因為這沒有任何意義,實現指的是抽象類和它的派生類用來實現自己的物件 由於實現的方式有多種,橋接模式的核心意圖就是把這些實現獨立出來,讓它們各自地變化,就使得每種實現的變化不會影響其他實現,從而達到應對變化的目的 注意:橋接模式與設計模式在程式碼上的實現時及其類似甚至相同的,不同點在於介面卡模式的介面卡類與被介面卡類是已經存在的,需要開發一個介面使得他們實現這個介面一起工作,而橋接模式是先定下介面策略,再讓不同的類去實現它。 public abstract class Abstraction { class AbstractionA extends Abstraction { public abstract class Implementor { public class BridgeClient { 將以上程式碼更加具體化到現實情況的程式碼如下: |
命令模式(Command)
將一個請求封裝為一個物件,從而使你可以用不同的請求對客戶進行引數化;對請求排隊或記錄請求日誌,以及支援可撤銷的操作 優點 1.降低物件之間的耦合度。 2.新的命令可以很容易地加入到系統中。 3.可以比較容易地設計一個組合命令。 4.呼叫同一方法實現不同的功能 缺點 使用命令模式可能會導致某些系統有過多的具體命令類。因為針對每一個命令都需要設計一個具體命令類,因此某些系統可能需要大量具體命令類,這將影響命令模式的使用。
|