導語:設計模式是無數碼農前人在實際的生產項目中通過不斷的踩坑、爬坑、修坑的經歷總結出來的經驗教訓,通過抽象以後表達成的概念。可以幫助後來的設計者避免重複一樣的錯誤或者彎路。我也抽空整理了一下設計模式,用本身的話總結了一下,自認爲通俗易懂。以爲有用的朋友能夠收藏一下。java
簡單工廠模式:程序員
包括三種角色,抽象產品、具體產品和工廠角色。其中在工廠直接完成對具體產品的建立。工廠模式的好處是須要建立對象的時候只須要輸入一個正確的參數就能夠得到所須要的對象,而無需知道其建立細節,這種模式將對象的建立和對象業務的處理分離,下降系統的耦合度,使得二者修改起來都相對容易。面試
工廠方法模式:算法
該模式包括四種角色,抽象工廠、具體工廠、抽象產品和具體產品角色。與簡單工廠模式相比,抽象工廠負責定義建立產品對象的公共接口,而工廠子類則負責生產具體的產品對象。這樣能夠將產品類的實例化操做延遲到工廠子類中完成。這樣,當須要生成一個具體產品對象時,首先要生成該對象的產品工廠。這樣的好處是可擴展性很好。數據庫
抽象工廠模式:編程
在工廠方法模式中,具體工廠負責生產具體的產品,每個具體工廠對應一種具體產品,工廠方法也具備惟一性,通常狀況下一個具體工廠中只有一工廠方法或者一組重載的工廠方法,可是有的時候咱們須要一個工廠能夠提供多個產品對象,而不是單一的產品對象。抽象工廠模式是爲了處理對象具備等級結構以及對象族的問題。對象的等級結構便是對象的繼承結構,好比電視機是一個抽象父類,其子類有海爾電視機、海信電視機、TCL電視機等。對象族:是指同一個工廠生產的、位於不一樣對象等級結構中的一組對象。如海爾生產的海爾電視機、海爾電冰箱,這兩個都位於海爾電器的產品族中,其中海爾電視機位於電視機等級結構中,海爾電冰箱位於電冰箱等級結構中。設計模式
抽象工廠模式包括抽象工廠、具體工廠、抽象產品、具體產品4中角色。每個具體工廠表明了一個對象族,都提供了多個工廠方法用於生產多種不一樣類型的產品,這些產品構成了一個對象族。api
使用抽象工廠模式建立對象的時候,首先建立對應該對象族的工廠,而後調用該工廠中建立該對象的方法。安全
建造者模式:性能優化
建造者模式講一個複雜對象的構建與它的表示分離,使得童顏的構建過程能夠建立不一樣的表示,它容許用戶只經過指定複雜對象的類型和內容就能夠建立它們,而不須要參與內部的具體構建細節。建造者模式包括4種角色:指揮者角色、抽象構造者角色、具體構造者角色以及產品角色。具體構造者負責生成具體的對象、指揮者一方面隔離了客戶和生產過程,另外一方面負責監控產品的生成過程,好比複雜對象的各個部分是以怎樣的順序生成等。指揮者針對抽象建造者編程,客戶端只須要知道具體構造者的類型,便可經過指揮者類調用構造者的相關方法,返回一個完整的產品對象。
距離說明,去KFC點餐的過程。一份套餐須要三個對象組成,即:可樂、漢堡和薯條。生產這些對象的師傅即爲具體建造者,服務員爲指揮者,套餐爲產品角色。顯然,師傅在後臺如何生產這些產品咱們並不知道,而是有服務員把這些對象構形成一份套餐。
原型模式:
原型設計模式:若是一些對象的建立構成比較複雜,並且有時候須要頻繁的建立,原型模式經過給出一個原型對象來指明索要建立的對象的類型,而後用複製這個原型的辦法建立出跟多同類型的對象。淺拷貝、深拷貝。
原型模式的適用場景:
Struts2中爲了保證線程安全性,Action對象的建立使用了原型模式,訪問一個已經存在的Action時將經過克隆的方式建立出一個新的對象。從而保證其中定義的變量無須進行加鎖實現同步,每個Action中都有本身的成員變量,避免Struts1因使用單例模式而致使的併發和同步問題。
Spring中,用戶也能夠採用原型來建立新的bean實例,從而實現每次獲取的是經過克隆生成的新實例,對其進行修改時對原有實例對象不形成任何影響。
單例設計模式:
單例模式確保某一個類只有一個實例,並且自行實例化並向整個系統提供這個實例,這個類成爲單例類。單例模式的實現通常是構造函數私有化、自行實例化一個實例並提供靜態函數供整個系統訪問這個實例。其應用場景包括:java.lang.Runtimr類、線程池ThreadPool類中。
適配器設計模式:
適配器模式是將一個類的接口轉換成客戶但願的另外一個接口,使得本來因爲接口不兼容而不能一塊兒工做的類能夠一塊兒工做。包括四個角色:客戶端、目標接口、適配器、適配者。
適用場景:已經存在的類的接口具備實用的功能可是不符合咱們的要求。好比咱們在程序中某個類C最終面對的是A接口或抽象類,所以類C只能使用A的子類或者實現類。假設咱們有B類含有特殊的操做或功能,如今咱們想在本身的系統中使用它,咱們能夠將其轉化成符合咱們的標準的類。這樣,C類就能夠在透明的狀況下任意的選擇使用C類的子類或者具備特殊功能的B類。根據轉化的方式能夠分爲類適配器和對象適配器。
類適配器:自定義適配器,該適配器繼承自B,而且實現了A,這樣根據多態性,類C就能夠很對該適配器編程。
對象適配器:適配器採用組合的形式,再也不繼承B,而是關聯一個B對象。
適配器模式的應用場景:Hibernate自帶的日誌系統是sel4j,自帶了一個jar包,slf4j-api-1.5.8.jar。打開這個jar包咱們發現只是定義了一些接口,沒有具體實現。若是想用slf4j日誌系統,須要引入slf4j-nop-1.5.8.jar實現jar包。可是這個日誌系統沒有log4j使用的普遍,所以Hibernate提供了一個jar包:slf4j-log4j12-1.5.8.jar,而後咱們再引入log4j.jar,這樣經過slf4j-log4j12-1.5.8.jar的適配做用,咱們就能夠經過調用slf4j的接口使用log4j的日誌功能。這是Hibernate中典型的適配器模式設計。
橋接模式:
橋接模式的設計思想是讓抽象部分與他的實現部分分離,使他們能夠獨立的變化。它主要應對的是:因爲實際的須要,某個類具備兩個或兩個以上的維度變化,若是隻是用繼承將沒法實現這種須要,或者使得設計變得至關臃腫。
好比說如今我要設計一個通用的日誌記錄工具。它支持數據庫記錄databaseLog和文本文件記錄FileLog兩種方式,同時既能夠運行在.net平臺和java平臺。按照繼承的思路,咱們首先抽象出一個Log基類,各類不一樣的日誌記錄方式都要繼承這個類。在這裏這兩種日誌格式都要繼承Log類。另外考慮到不一樣平臺的日誌記錄,對於操做數據庫、寫入文本文件鎖調用的方式多是不同的,所以須要提供各類不一樣平臺上的實現,對上面的類所以獲得四種繼承方式。可是,這種格式使得設計變得至關臃腫。若是我有5中日誌格式、5中運行平臺,則須要25中繼承方式。咱們能夠看到這種臃腫的繼承關係形成的根源在於引發Log變化的緣由有兩個,即日誌記錄方式的變化和日誌記錄平臺的變化。如今咱們要解耦這兩個方向的變化,把他們以前強耦合的繼承關係便爲弱耦合的關聯關係。這就是橋接模式。
咱們能夠把記錄記錄格式和日記記錄在不一樣平臺的實現分別看成兩個獨立的部分來對待,對於日誌記錄方式,類結構還是databaseLog類和FileLog兩個類繼承Log類。對於日誌記錄平臺,引入另一個抽象類,ImpLog,它是日誌記錄在不一樣平臺上的實現的基類。這個時候,對於日誌記錄方式和不一樣的運行平臺這兩個類能夠獨立的變化了。咱們要作的就是把這兩個部分鏈接起來。在這裏使用對象組合的方式,即在Log類中關聯一個ImpLog對象。能夠看到,經過對象組合的方式,Bridge模式把兩個角色之間的繼承關係改成了耦合的關係,從而使這二者能夠從容自若的各自獨立的變化,這也是Bridge模式的本意。
一、JDBC驅動程序也是橋接模式的應用之一。使用JDBC驅動程序的應用系統就是抽象角色,而所使用的數據庫是實現角色。一個JDBC驅動程序能夠動態地將一個特定類型的數據庫與一個Java應用程序綁定在一塊兒,從而實現抽象角色與實現角色的動態耦合。
組合模式:
組合模式描述瞭如何將容器對象和葉子對象進行遞歸組合,使得用戶在使用時無須對它們進行區分,能夠一致的對待容器對象和葉子對象。組合多個對象造成鼠樹形結構以表示總體部分的層次結構。組合模式對但對象(即葉子對象)和組合對象(容器對象)的使用具備一致性。組合模式包括抽象組件、葉子組件和容器組件以及客戶類。
組合模式的關鍵是定義了一個抽象類,他既能夠表明葉子,也能夠表明容器,而客戶端針對該抽象組件進行編程,無須知道它到底表示的是葉子仍是容器,能夠對其進行統一的處理。同時容器對象與抽象構件之間還存在一個聚合關聯關係,在容器對象中既能夠包含葉子也能夠包含容器。好比文件系統的UML圖。
實現組合模式有兩種思路(1)在抽象組件中定義葉子節點和容器節點公共的方法,好比定義一個表示文件系統節點的抽象組件,能夠只定義顯示名稱的方法,這樣在容器組件中擴展抽象組件的方法。(2)在抽象組件中明肯定義容器節點全部的方法,這些方法有些確定是葉子節點不能使用的,好比文件系統中葉子節點不會有remove刪除一個文件的方法。這時候抽象組件通常是一個抽象類,抽象類中對這些方法作了通常的處理,而在容器節點中根據功能進行相應的覆蓋。Java中XML文檔解析以及Java的AWT/SWing均用到了組合模式。
裝飾設計模式:
當想要對已有的對象進行功能加強時,能夠自定義類,將原有對象傳入,基於已有的功能,並提供增前功能,那麼自定義的該類稱爲裝飾類(裝飾器)。裝飾器會經過構造方法接收被裝飾的類,並基於被裝飾的類提供更強的功能。
裝飾設計模式包括抽象構件、具體構件、抽象裝飾類和具體裝飾類。這裏的裝飾類就是對具體構件的加強,所以和具體構建同樣都是繼承與抽象裝飾器。
Java中使用的最普遍的裝飾器模式就是JavaIO類的設計。好比,OutPutStream是輸出流的基類,其子類有FileOutputStream 和FilterOutputStream,而FilterOutputStream的子類有BufferedOutputStream和DataOutputStream兩個子類。其中,FileOutputStream爲系統的核心類,它實現了向文件寫數據的功能,使用DataOutputStream能夠在FileOutputStream的基礎上增長多種數據類型的寫操做支持(DataOutputStream類中有writeUTF、writeInt等函數),而BufferdOutputStream裝飾器能夠對FileOutputStream增長緩衝功能,優化I/O性能。
外觀模式:
一個公司的門戶網站就是一個外觀模式的應用。考慮網站導航,有公司新聞、留言系統、產品介紹、在線論壇等導航。這樣咱們先經過導航定位到不一樣的頁面,這樣總比直接找到某一頁面方便。外觀模式定義爲:外部與一個子系統的通訊必須經過一個統一的外觀對象進行,爲子系統中的一組接口提供一個一致的界面,外觀模式定義了一個高層接口,這個接口使得下降了用戶和這些子系統的耦合度。
外觀設計模式的的設計目標是使系統間的通訊和相互依賴關係達到最小,而達到這個目標的途徑之一就是引入外觀對象,它爲 子系統的訪問提供了一個簡單而單一的入口,同時下降了客戶類與子系統類的耦合度。
在java中的應用:jdbc數據庫操做,提供一個統一的類來管理對數據庫的打開、查詢和關閉操做。
享元設計模式:
若是系統中對象數量太多時,因爲建立和銷燬對象都是很消耗資源的操做,不斷的產生新對象將致使運行代價太高,帶來性能降低等問題。享元模式正是爲解決這類問題而誕生的。享元模式經過共享技術實現對象的重用。在享元模式中一般建立一個享元工廠來維護一個享元池用於存儲具備相同內部狀態的享元對象。好比說片5000個字母組成的英文文章,咱們只須要保存26個字母對象和一些標點符號對象集合,而不是保存5000個對象。由於相同的字母其內部狀態(好比其ascii碼相同)相同,而外部狀態,好比這個字母相對文章開始的偏移量不一樣,所以這些相同的字母是能夠共享的,只是在排列的時候被放到了不一樣的位置。
享元模式包括四種角色,抽象享元類、具體享元類、非共享具體享元類和享元工廠類。其中享元工廠類維護一個享元抽象類的hashMap,當用戶須要具體享元對象的時候,不是直接建立對象,而是從享元工廠的這個hashMap中尋找時候有相同內部狀態的對象,若是有則直接返回;若是沒有則建立並加入到這個集合後才返回。
Java中應用享元設計模式的場景:JDK中定義String類採用了享元設計模式。Jdk5的併發框架的線程池、以及Integer自動裝箱(-128~127的整數)用到了享元設計模式,
代理設計模式:
在某些狀況下,一個客戶不想直接引用一個對象,此時能夠經過一個代理的第三者來實現間接引用,能夠在客戶端和目標對象之間起到中介的做用,而且能夠經過生成代理增長或者刪除某些功能。
代理模式包括抽象主題角色、代理主題角色、和真實主題角色。抽象主題是真實主題和代理主題的共同接口,使得在任何使用真實主題的地方均可以使用代理主題(根據里氏代換原則),客戶端一般須要針對抽象主題角色編程。
代理模式的應用:
一、JavaRMI的使用。
二、Spring框架中AOP技術也是代理的應用,在Spring AOP中使用動態代理技術。
三、Hibernate根據id進行load查詢時,也使用到了代理,用於延遲加載。此時,HIbernate使用一個動態代理子類替代用戶定義的類,這樣在載入對象時,就沒必要初始化對象的全部信息。經過代理,攔截原有的getter方法,能夠在真正使用對象數據時,才能從數據庫加載實際的數據,從而提高。
職責鏈設計模式:
系統中有不少能處理請求的對象。指責連模式將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,知道有一個對象處理這個請求爲止。這樣,客戶端無須關心請求的處理細節以及請求的傳遞,只需將請求發送到鏈上便可,將請求的發送者個處理者解耦。
設計職責鏈時,→每個對象及對其上級領導的引用而鏈接起來造成一條鏈。請求在這條鏈上傳遞,直到鏈上的某一個對象處理此請求爲止,發出這個請求的客戶端並不知道鏈上哪個對象最終處理這個請求,這使得系統能夠在不影響客戶端的狀況下動態的從新分配和組織責任。
職責鏈模式在Java中使用最多的就是Java的異常處理機制。
觀察者模式:
觀察者模式創建一種對象與對象之間的依賴關係,一個對象發生改變時將自動通知其餘對象,其餘對象將相應作出反應。
觀察者模式包括四種角色:目標、具體目標、觀察者、抽象觀察者。
應用:
一、MVC框架,觀察目標爲MVC中的model,而觀察者就是MVC的View,而Controller充當二者之間的中介者,當模型層的數據發生改變時,試圖自動改變其顯式內容。
二、Java NIO中的非阻塞通道。SelectableChannel能夠向Selector註冊讀就緒和寫就緒等事件。Selector負責監控這些事件,等到事件發生時,好比發生了讀就緒事件,SelectableChannel就能夠執行讀操做了。在這種通訊模式中,Selector就充當了觀察者。
三、Java語言提供了對觀察者模式的支持:在就JDK的java.util包中,提供了Observable目標類和Observer接口(抽象觀察者)。
模版設計模式:
須要定義一些頂級邏輯,或者是一個操做中算法的骨架,但願一些步驟的執行推遲到子類中。模版模式涉及到兩個角色,抽象模版和具體模版。抽象模版中定義了一個或多個操做,以便於讓子類實現,這些操做是一個算法的邏輯和框架。以及一個模版方法,該模版方法是一個具體方法,它給出了抽象操做的實現邏輯。具體模版實現父類所定義的一個或多個抽象方法,它們是一個頂級邏輯的組成步驟。
使用場景:
一、模版方法模式普遍應用於框架設計(如Spring,Struts等)中,以保證父類控制處理路程的邏輯順序,好比Spring對於Hibernate使用的簡單封裝:HibernateTemplate。
二、Java單元測試工具JUnit中的TestCase類的設計。
命令設計模式:
在面向對象的程序設計中,一個對象調用另外一個對象,通常狀況下的調用過程是:建立目標對象實例、設置調用參數、調用目標對象的方法。可是有些狀況下有必要使用一個專門的類對這種調用過程加以封裝,這種類叫作command類。
命令模式的本質是對命令進行封裝,將發出命令的責任個執行命令的責任分隔開。每個命令都是一個操做,請求的一方發出請求,要求執行一個動做,接收的一方收到請求,並執行操做。命令模式容許請求的一方和接收的一方獨立開來,使得請求的一方沒必要知道接受請求的一方的接口,更沒必要知道請求是怎樣被接收,以及操做是否被執行、什麼時候被執行以及怎樣被執行的。命令模式的關鍵在於引入了抽象命令接口,且發送者針對命令接口編程,只有實現了抽象命令接口的具體命令才能被接受者相關聯。
命令模式包括5個角色:
命令角色,聲明執行操做的接口,通常爲接口或抽象類。
具體命令角色:將一個接收者對象綁定於一個動做,調用接收者相應的操做,以實現命令角色生命的執行操做的接口。
客戶角色:建立一個具體命令對象,並能夠設定它的接收者。
調用者角色:調用命令對象執行這個請求。
接收者角色:知道如何實施與執行一個請求相關的操做。任何類均可以做爲一個接收者。
適用場景:現實中的遙控器。
中介者模式:
在面向對象的軟件設計與開發過程當中,根據單一職責原則,咱們應該將對象細化,使其只負責或呈現單一的職責。對於一個模塊,可能有不少對象構成,並且這些對象之間可能存在相互的引用,爲了減小對象之間複雜的引用關係,使之成爲一個鬆耦合的系統,咱們須要使用中介者模式。
中介者模式包括抽象中介者、具體中介者、抽象同事類、具體同事類。
這裏餓中介者有兩方面的做用:中轉做用。經過中轉做用,各個同事對象再也不須要顯示的引用其餘同事。同事之間通訊時候經過中介者便可。協調做用。中介者能夠更進一步的對同事之間的關係進行封裝。
適用場景:
一、MVC 是Java EE 的一個基本模式,此時控制器Controller做爲一種中介者,它負責控制視圖對象View和模型對象Model之間的交互。如在Struts中,Action就能夠做爲JSP頁面與業務對象之間的中介者。
二、QQ羣原理。QQ羣做爲人與人之間的中介者,把人組織起來,負責人與人之間的交互。
狀態模式:
不少狀況下,一個對象的行爲取決於一個或多個動態變化的屬性,這樣的屬性叫作狀態。當一個這樣的對象與外部事件產生交互時,其內部狀態就會改變,使得系統的行爲也隨之發生變化。
好比,以論壇用戶等級爲例。在這個論壇系統中,用戶發表留言、回覆留言增長積分;用戶下載文件將扣除積分。假設將用戶等級分爲初級用戶、中級用戶和高級用戶。若是用戶積分小於100分,將處於初級用戶狀態,此時不能下載文件。若是積分大於100分但小於1000分,則爲中級狀態,用戶能夠下載文件,並且發表留言的時候能夠獲取雙倍積分。若積分大於1000,不但發表留言獲取雙倍積分,並且下載文件只扣除一半積分。這種狀況下,用戶只須要進行正常的論壇操做,回覆帖子、發帖子、下載等操做,系統會根據積分狀態自動轉換到相應的狀態。
像這種,用戶的行爲隨其等級不一樣而發生改變的場景可使用狀態模式。
狀態模式包括環境類、抽象狀態類、具體狀態類。通常環境類中會關聯一個狀態類。
使用場景:
在政府OA辦公系統中,一個批文的狀態有多種:還沒有辦理;正在辦理;正在批示;正在審覈;已經完成等各類狀態,並且批文狀態不一樣時對批文的操做也有所差別。使用狀態模式能夠描述工做流對象(如批文)的狀態轉換以及不一樣狀態下它所具備的行爲。
策略模式:
完成一項任務,每每能夠有多種不一樣的方式,每一種方式稱爲一個策略,咱們能夠根據環境或者條件的不一樣選擇不一樣的策略來完成該項任務。在軟件開發中也經常遇到這種狀況,實現某個功能有多個路徑,此時可使用一種設計模式來使得系統能夠靈活地選擇解決途徑,特可以方便的增長新的解決途徑。
定義一系列算法,將每個算法封裝起來,並讓它們能夠相互替換。此模式讓算法的變化不會影響到使用算法的用戶。
策略模式包括環境類、抽象策略類、具體策略類。
迭代器模式:
一個聚合對象,例如一個列表(List)或者一個集合(Set)應該提供一種方法來讓別人能夠訪問它的元素,而又不須要它的內部結構。針對不一樣的須要,可能還要以不一樣的方式遍歷整個聚合對象,可是咱們並不但願在聚合對象的抽象噌接口中充斥者各類不一樣遍歷的操做。怎樣一個聚合對象,又不須要了解聚合對象的內部結構,還能提供多種不一樣的遍歷方式,便是迭代器的動機。
迭代器模式包括抽象聚合類、具體聚合類、抽象迭代器、具體迭代器。
想要了解更多設計模式詳解、架構技術知識點的,能夠關注我一下,我後續也會整理更多這一塊的知識點分享出來,另外順便給你們推薦一個交流學習羣:650385180,裏面會分享一些資深架構師錄製的視頻錄像:有Spring,MyBatis,Netty源碼分析,高併發、高性能、分佈式、微服務架構的原理,JVM性能優化這些成爲架構師必備的知識體系。還能領取免費的學習資源,目前受益良多,如下的課程體系圖也是在羣裏獲取。
總結:
以上就是我要說的內容,但願以上的內容能夠幫助到正在默默艱辛,遇到瓶疾且不知道怎麼辦的Java程序員們,我能幫你的只有這麼多了,但願你們在日後的工做與面試中,一切順利。