天天看點

Java中十種常見的設計模式

1. 單例模式

  1. 實作方式:

    (1)将被實作的類的構造方法設計成private的。

    (2)添加此類引用的靜态成員變量,并為其執行個體化。

    (3)在被實作的類中提供公共的CreateInstance函數,傳回執行個體化的此類,就是b中的靜态成員變量。

  2. 優點:

    (4)在單例模式中,活動的單例隻有一個執行個體,對單例類的所有執行個體化得到的都是相同的一個執行個體。這樣就防止其它對象對自己的執行個體化,確定所有的對象都通路一個執行個體

    (5)單例模式具有一定的伸縮性,類自己來控制執行個體化程序,類就在改變執行個體化程序上有相應的伸縮性。

    (6)提供了對唯一執行個體的受控通路。

    (7)由于在系統記憶體中隻存在一個對象,是以可以節約系統資源,當需要頻繁建立和銷毀的對象時單例模式無疑可以提高系統的性能。

    (8)允許可變數目的執行個體。

    (9)避免對共享資源的多重占用。

  3. 缺點:

    (1)不适用于變化的對象,如果同一類型的對象總是要在不同的用例場景發生變化,單例就會引起資料的錯誤,不能儲存彼此的狀态。

    (2)由于單利模式中沒有抽象層,是以單例類的擴充有很大的困難。

    (3)單例類的職責過重,在一定程度上違背了“單一職責原則”。

    (4)濫用單例将帶來一些負面問題,如為了節省資源将資料庫連接配接池對象設計為的單例類,可能會導緻共享連接配接池對象的程式過多而出現連接配接池溢出;如果執行個體化的對象長時間不被利用,系統會認為是垃圾而被回收,這将導緻對象狀态的丢失。

  4. 使用注意事項:

    (1)使用時不能用反射模式建立單例,否則會執行個體化一個新的對象

    (2)使用懶單例模式時注意線程安全問題

    (3)單例模式和懶單例模式構造方法都是私有的,因而是不能被繼承的,有些單例模式可以被繼承(如登記式模式)

  5. 适用場景:

    單例模式隻允許建立一個對象,是以節省記憶體,加快對象通路速度,是以對象需要被公用的場合适合使用,如多個子產品使用同一個資料源連接配接對象等等。如:

    (1)需要頻繁執行個體化然後銷毀的對象。

    (2)建立對象時耗時過多或者耗資源過多,但又經常用到的對象。

    (3)有狀态的工具類對象。

    (4)頻繁通路資料庫或檔案的對象。

    以下都是單例模式的經典使用場景:

    (1)資源共享的情況下,避免由于資源操作時導緻的性能或損耗等。如上述中的日志檔案,應用配置。

    (2)控制資源的情況下,友善資源之間的互相通信。如線程池等。

  6. 應用場景舉例:

    (1)外部資源:每台計算機有若幹個列印機,但隻能有一個PrinterSpooler,以避免兩個列印作業同時輸出到列印機。内部資源:大多數軟體都有一個(或多個)屬性檔案存放系統配置,這樣的系統應該有一個對象管理這些屬性檔案

    (2)Windows的TaskManager(任務管理器)就是很典型的單例模式,不能同時打開兩個windows task manager。

    (3)windows的Recycle Bin(資源回收筒)也是典型的單例應用。在整個系統運作過程中,資源回收筒一直維護着僅有的一個執行個體。

    (4)網站的計數器,一般也是采用單例模式實作,否則難以同步。

    (5)應用程式的日志應用,一般都何用單例模式實作,這一般是由于共享的日志檔案一直處于打開狀态,因為隻能有一個執行個體去操作,否則内容不好追加。

    (6)Web應用的配置對象的讀取,一般也應用單例模式,這個是由于配置檔案是共享的資源。

    (7)資料庫連接配接池的設計一般也是采用單例模式,因為資料庫連接配接是一種資料庫資源。資料庫軟體系統中使用資料庫連接配接池,主要是節省打開或者關閉資料庫連接配接所引起的效率損耗,這種效率上的損耗還是非常昂貴的,因為何用單例模式來維護,就可以大大降低這種損耗。

    (8)多線程的線程池的設計一般也是采用單例模式,這是由于線程池要友善對池中的線程進行控制。

    (9)作業系統的檔案系統,也是大的單例模式實作的具體例子,一個作業系統隻能有一個檔案系統。

    (10)HttpApplication也是機關例的典型應用。熟悉ASP.Net(IIS)的整個請求生命周期的人應該知道HttpApplication也是單例模式,所有的HttpModule都共享一個HttpApplication執行個體.

2. 政策模式

  1. 實作方式:

    (1)提供公共接口或抽象類,定義需要使用的政策方法。(政策抽象類)

    (2)多個實作的政策抽象類的實作類。(政策實作類)

    (3)環境類,對多個實作類的封裝,提供接口類型的成員量,可以在用戶端中切換。

    (4)用戶端調用環境類進行不同政策的切換。

    注:Jdk中的TreeSet和TreeMap的排序功能就是使用了政策模式。

  2. 政策模式的優點

    (1)政策模式提供了管理相關的算法族的辦法。政策類的等級結構定義了一個算法或行為族。恰當使用繼承可以把公共的代碼移到父類裡面,進而避免代碼重複。

    (2)使用政策模式可以避免使用多重條件(if-else)語句。多重條件語句不易維護,它把采取哪一種算法或采取哪一種行為的邏輯與算法或行為的邏輯混合在一起,統統列在一個多重條件語句裡面,比使用繼承的辦法還要原始和落後。

  3. 政策模式的缺點

    (1)用戶端必須知道所有的政策類,并自行決定使用哪一個政策類。這就意味着用戶端必須了解這些算法的差別,以便适時選擇恰當的算法類。換言之,政策模式隻适用于用戶端知道算法或行為的情況。

    (2)由于政策模式把每個具體的政策實作都單獨封裝成為類,如果備選的政策很多的話,那麼對象的數目就會很可觀。

3. 代理模式

  1. 靜态代理

    實作方式:

    (1)為真實類和代理類提供的公共接口或抽象類。(租房)

    (2)真實類,具體實作邏輯,實作或繼承a。(房主向外租房)

    (3)代理類,實作或繼承a,有對b的引用,調用真實類的具體實作。(中介)

    (4)用戶端,調用代理類實作對真實類的調用。(租客租房)

  2. 動态代理

    實作方式:

    (1)公共的接口(必須是接口,因為Proxy類的newproxyinstance方法的第二參數必須是個接口類型的Class)

    (2)多個真實類,具體實作的業務邏輯。

    (3)代理類,實作InvocationHandler接口,提供Object成員變量,和Set方法,便于用戶端切換。

    (4)用戶端,獲得代理類的執行個體,為object執行個體指派,調用Proxy.newproxyinstance方法在程式運作時生成繼承公共接口的執行個體,調用相應方法,此時方法的執行由代理類實作的Invoke方法接管。

    jdk動态代理使用的局限性:

    通過反射類Proxy和InvocationHandler回調接口實作的jdk動态代理,要求委托類必須實作一個接口,但事實上并不是所有類都有接口,對于沒有實作接口的類,便無法使用該方方式實作動态代理。

4. 觀察者模式

觀察者模式是對象的行為模式,又叫釋出-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監聽器(Source/Listener)模式或從屬者(Dependents)模式。

  1. 實作方式:

    (1)角色抽象類(提供對觀察者的添加,删除和通知功能)。

    (2)角色具體類,實作a,維護一個c的集合(對角色抽象類的實作)。

    (3)觀察者抽象類(被角色通知後實作的方法)。

    (4)觀察者實作類,實作c(多個)。

    注:JDK提供了對觀察者模式的支援,使用Observable類和Observer接口

  2. 兩種模型(推模型和拉模型):

    (1)推模型是假定主題對象知道觀察者需要的資料;而拉模型是主題對象不知道觀察者具體需要什麼資料,沒有辦法的情況下,幹脆把自身傳遞給觀察者,讓觀察者自己去按需要取值。

    (2)推模型可能會使得觀察者對象難以複用,因為觀察者的update()方法是按需要定義的參數,可能無法兼顧沒有考慮到的使用情況。這就意味着出現新情況的時候,就可能提供新的update()方法,或者是幹脆重新實作觀察者;而拉模型就不會造成這樣的情況,因為拉模型下,update()方法的參數是主題對象本身,這基本上是主題對象能傳遞的最大資料集合了,基本上可以适應各種情況的需要。

5. 裝飾模式

  1. 實作方式:

    (1)抽象的被裝飾角色(所有的角色都要直接或間接的實作本角色)

    (2)具體的被裝飾角色,實作或繼承a(被功能擴充的角色)

    (3)裝飾角色,實作或繼承a(本類有對a的引用,所有的具體裝飾角色都需要繼承這個角色)

    (4)多個具體修飾角色,繼承c(對被裝飾角色的功能擴充,可以任意搭配使用)

  2. 意圖:

    動态地給一個對象添加一些額外的職責。就增加功能來說,Decorator模式相比生成子類更為靈活。該模式以對用戶端透明的方式擴充對象的功能。

  3. 适用環境:

    (1)在不影響其他對象的情況下,以動态、透明的方式給單個對象添加職責。

    (2)處理那些可以撤消的職責。

    (3)當不能采用生成子類的方法進行擴充時。一種情況是,可能有大量獨立的擴充,為支援每一種組合将産生大量的子類,使得子類數目呈爆炸性增長。另一種情況可能是因為類定義被隐藏,或類定義不能用于生成子類。

6. 擴充卡模式

擴充卡模式把一個類的接口變換成用戶端所期待的另一種接口,進而使原本因接口不比對而無法在一起工作的兩個類能夠在一起工作。

  1. 類擴充卡(子類繼承方式)

    實作方式:

    (1)目标抽象角色(定義客戶要用的接口)

    (2)擴充卡(實作a繼承c,作為一個轉換器被客戶調用)

    (3)待擴充卡(真正需要被調用的)

    (4)用戶端(借用a的執行個體調用c的方法)

  2. 對象擴充卡(對象的組合方式)

    實作方式:

    (1)目标抽象角色(定義客戶要用的接口)

    (2)擴充卡(實作a,維護一個c的引用,作為一個轉換器被d調用)

    (3)待擴充卡(真正需要被調用的)

    (4)用戶端(此類,借用a類的執行個體調用c類的方法,類似靜态代理,但是解決的問題不同)

  3. 預設的方式

    實作方式:

    (1)抽象接口

    (2)實作a的擴充卡類(空實作)

    (3)用戶端,繼承b,調用b中的方法,不必直接實作a(直接實作a需要實作a中的所有的方法)

  4. 擴充卡模式的優點:

    (1)更好的複用性

    系統需要使用現有的類,而此類的接口不符合系統的需要。那麼通過擴充卡模式就可以讓這些功能得到更好的複用。

    (2)更好的擴充性

    在實作擴充卡功能的時候,可以調用自己開發的功能,進而自然地擴充系統的功能。

  5. 擴充卡模式的缺點:

    過多的使用擴充卡,會讓系統非常零亂,不易整體進行把握。比如,明明看到調用的是A接口,其實内部被适配成了B接口的實作,一個系統如果太多出現這種情況,無異于一場災難。是以如果不是很有必要,可以不使用擴充卡,而是直接對系統進行重構。

7. 指令模式

将一個請求封裝為一個對象,進而可用不同的請求對客戶進行參數化;對請求排隊或記錄日志,以及支援可撤銷的操作;将“送出請求的對象”和”接收與執行這些請求的對象”分隔開來。

  1. 實作方式:

    (1)抽象的指令角色,如:菜單(規定可以點哪些菜)

    (2)具體的指令角色(實作a維護一個對c的引用),如:訂單(已點的菜)

    (3)接收者(具體執行指令的角色),實際操作時,很常見使用"聰明"指令對象,也就是直接實作了請求,而不是将工作委托給c(弊端?)如:廚師接收訂單後做菜

    (4)調用者(維護一個對a的引用),如:服務員負責點菜并把訂單推給廚師

    (5)用戶端調用d發出指令進而執行c的方法,如:顧客點餐

  2. 效果:

    (1)command模式将調用操作的對象和實作該操作的對象解耦

    (2)可以将多個指令裝配成一個複合指令,複合指令是Composite模式的一個執行個體

    (3)增加新的command很容易,無需改變已有的類

    3.适用性:

    (1)抽象出待執行的動作以參數化某對象

    (2)在不同的時刻指定、排列和執行請求。如請求隊列

    (3)支援取消操作

    (4)支援修改日志

    (5)用建構在原語操作上的高層操作構造一個系統。支援事物

8. 組合模式

将對象組合成樹形結構以表示“部分整體”的層次結構。組合模式使得使用者對單個對象和複雜對象的使用具有一緻性。

  1. 實作方式:

    (1)抽象的構件接口(規範執行的方法),b及c都需實作此接口,如:Junit中的Test接口

    (2)葉部件(實作a,最小的執行機關),如:Junit中我們所編寫的測試用例

    (3)組合類(實作a并維護一個a的集合[多個b的組合]),如:Junit中的TestSuite

    (4)用戶端可以随意的将b和c進行組合,進行調用

  2. 什麼情況下使用組合模式:

    當發現需求中是展現部分與整體層次結構時,以及你希望使用者可以忽略組合對象與單個對象的不同,統一地使用組合結構中的所有對象時,就應該考慮組合模式了。

9. 簡單工廠模式

就是建立一個工廠類,對實作了同一接口的一些類進行執行個體的建立。簡單工廠模式的實質是由一個工廠類根據傳入的參數,動态決定應該建立哪一個産品類(這些産品類繼承自一個父類或接口)的執行個體。

  1. 實作方式:

    (1)抽象産品類(也可以是接口)

    (2)多個具體的産品類

    (3)工廠類(包括建立a的執行個體的方法)

  2. 優點:

    工廠類是整個模式的關鍵.包含了必要的邏輯判斷,根據外界給定的資訊,決定究竟應該建立哪個具體類的對象.通過使用工廠類,外界可以從直接建立具體産品對象的尴尬局面擺脫出來,僅僅需要負責“消費”對象就可以了。而不必管這些對象究竟如何建立及如何組織的.明确了各自的職責和權利,有利于整個軟體體系結構的優化。

  3. 缺點:

    由于工廠類集中了所有執行個體的建立邏輯,違反了高内聚責任配置設定原則,将全部建立邏輯集中到了一個工廠類中;它所能建立的類隻能是事先考慮到的,如果需要添加新的類,則就需要改變工廠類了。當系統中的具體産品類不斷增多時候,可能會出現要求工廠類根據不同條件建立不同執行個體的需求.這種對條件的判斷和對具體産品類型的判斷交錯在一起,很難避免子產品功能的蔓延,對系統的維護和擴充非常不利;

10. 模闆方法模式

  1. 實作方式:

    (1)父類模闆類(規定要執行的方法和順序,隻關心方法的定義及順序,不關心方法實作)

    (2)子類實作類(實作a規定要執行的方法,隻關心方法實作,不關心調用順序)

  2. 優點:

    (1)封裝不變部分,擴充可變部分:把認為不變部分的算法封裝到父類實作,可變部分則可以通過繼承來實作,很容易擴充。

    (2)提取公共部分代碼,便于維護。

    (3)行為由父類控制,由子類實作。

  3. 缺點:

    模闆方法模式颠倒了我們平常的設計習慣:抽象類負責聲明最抽象、最一般的事物屬性和方法,實作類實作具體的事物屬性和方法。在複雜的項目中可能會帶來代碼閱讀的難度。