天天看點

建立型模式之單例模式與工廠模式(一)

  建立型模式,就是建立對象的模式,抽象了執行個體化的過程。它幫助一個系統獨立于如何建立、組合和表示它的那些對象。關注的時對象的建立,建立型模式将建立對象的過程進行了抽象,也可以了解為将建立對象的過程進行了封裝,作為客戶程式僅僅需要去使用對象,而不再關心建立過程中的邏輯。

  具體的建立型模式可分為:

單例模式(Singleton)

簡單工廠模式(Simple Factory)

工廠方法模式(Factory Method)

抽象工廠模式(Abstract Factory)

原型模式(Prototype)

建造者模式(Builder)

  所謂的單例設計模式,就是采取一定的方法保證在整個的軟體系統中,對某個類隻能存在一個對象執行個體,并且該類隻提供一個取得其對象執行個體的方法。例如:Hibernate的SessionFactory,它充當資料存儲源的代理,并負責建立Session對象。SessionFactory并不是輕量級的,一般情況下,一個項目通常隻需要一個SessionFactory就夠,這時就需要使用單例模式。

  單例設計模式有八種方式(推薦使用1、2、6、7、8):

餓漢式(靜态常量)

餓漢式(靜态代碼塊)

懶漢式(線程不安全)

懶漢式(線程安全,同步方法)

懶漢式(線程不安全,同步代碼塊)

雙重檢查

靜态内部類

枚舉

  優點:這種寫法比較簡單,就是在類裝載的時候就完成執行個體化,避免了線程同步問題。

  缺點:在類裝載的時候就完成執行個體化,沒有達到Lazy Loading的效果。如果從始至終從未使用過這個執行個體,則會造成記憶體的浪費。

  結論:這種單例模式可用,但是可能造成記憶體浪費

  優缺點:這種方式和上面的方式其實類似,隻不過将類執行個體化的過程放在了靜态代碼塊中,也是在類裝載的時候,就執行靜态代碼塊中的代碼,初始化類的執行個體。優缺點和上面是一樣的。

  結論:這種單例模式可用,但是可能造成記憶體浪費。

起到了LazingLoading的效果(使用時才建立),但是隻能在單線程下使用。

如果在多線程下,多個線程同時判斷singleton為null,會建立多個執行個體。

結論:在實際開發中,不要使用這種方式建立

采用同步方法關鍵字synchronized解決線程安全問題

效率太低了,每個線程在想獲得類的執行個體時候,執行getInstance()方法都要進行同步。而其實這個方法隻執行一次執行個體化代碼就夠了,後面的想獲得該類執行個體,直接return就行了。

結論:實際開發中,不推薦使用這種方式

這種方式本意是想對6方法進行改進,因為前面同步方法效率太低了,改為同步産生執行個體化的代碼塊

但是這種同步并不能起到線程同步的作用(解決不了線程安全的問題)。因為同樣如果多個線程進行判斷singleton為null時,雖然會在synchronized方法外被阻塞,但是阻塞完成之後還是會繼續執行産生多個不同執行個體對象

結論:在實際開發中,不能使用這種方式

第一個if判斷能提升建立效率,如果去掉,多線程會阻塞;

第二個if判斷能解決線程安全問題,如果去掉會有多個線程進入synchronized代碼塊中建立;

synchronized方法能保順序執行,如果去掉也會建立多個執行個體;

volatile關鍵字能保證可見性和禁止指令重排,詳細點選Volatile的應用DCL單例模式(四)

結論:實作了線程安全;延遲加載;效率較高。推薦使用

當外部類Singleton進行類轉載時,靜态内部是不會被裝載的

當調用Singleton的getInstance()方法,用到INSTANCE靜态常量時,靜态類才會被裝載,且隻會裝載一次。在裝載時,線程是安全的

這種方式采用了類裝載的機制來保證初始化執行個體時隻會有個一個線程

靜态内部類方式在Singleton類被裝載時并不會立即執行個體化,而是需要執行個體化時,調用getInstance()方式,才會裝載類StaticClass類,進而完成Singleton的執行個體化

類的靜态屬性隻會在第一次加載類的時候初始化,是以在這裡,JVM幫助我們保證了線程的安全性,在類進行初始化時,别的線程是無法進入的

優點:避免了線程不安全,利用靜态内部類特點實作延遲加載,效率高

結論:推薦使用

這是借助JDK1.5中添加的枚舉來實作單例模式。不僅能避免多線程同步問題,而且還能防止反序列化重新建立新的對象

  Runtime源碼

  

建立型模式之單例模式與工廠模式(一)

單例模式保證了系統記憶體中改類隻存在一個對象,節省了系統資源,對于一些需要頻繁建立銷毀的對象,使用單例模式可以提高系統性能

當想執行個體化一個單例類的時候,必須要記住使用相應的擷取對象的方法,而不是使用new

單例模式使用的場景:需要頻繁建立和銷毀的對象、建立對象時耗時過多或耗費資源過多(即:重量級對象),但又經常用到的對象、工具類對象、頻繁通路資料庫或檔案的對象(比如資料源、session工廠等)

需求:

1) 披薩的種類很多(比如 GreekPizza、CheesePizza 等)

2) 披薩的制作有 prepare,bake, cut, box

3) 完成披薩店訂購功能。

源碼:詳情

建立型模式之單例模式與工廠模式(一)

優點是比較好了解,簡單易操作

缺點是違反了ocp原則,對擴充開發對修改關閉。當需要添加新的pizza種類時需要修改調用方OrderPizza(如果有多個調用方OrderPizza1,OrderPizza2...)。

  把建立Pizza對象封裝到一個類(工廠類)中,在訂購時隻需要根據不同的orderType就能獲得對應的Pizza類。

建立型模式之單例模式與工廠模式(一)

  如果需要重新添加一個pizza的種類,需要在添加一個類繼承Pizza類(可選擇重寫方法),在簡單工廠中添加對應的建立邏輯。不需要在每個OrderPizza類中修改代碼。

  需求變動:客戶在點披薩時,可以點不同口味的披薩,比如 北京的奶酪 pizza、北京的胡椒 pizza 或者是倫敦的奶酪 pizza、倫敦的胡椒 pizza。

   

建立型模式之單例模式與工廠模式(一)

如果這種情況還是使用簡單工廠模式,建立不同的工廠類,比如BJPizzaSimpleFactory、LDPizzaSimpleFactory等,從目前的需求分析是可以的,但是會導緻有很多的工廠類,考慮到軟體的可維護性、可擴充性,是以選擇工廠方法模式

工廠方法模式:定義了一個建立對象的抽象方法,由子類決定要執行個體化的類。工廠方法模式将對象的執行個體化推遲到子類。

  抽象工廠模式:定義了一個interface用于建立相關或有依賴關系的對象簇,而無需指明具體的類。抽象工廠模式可以将簡單工廠模式和工廠方法模式進行整合。

  從設計層面看,抽象工廠模式就是對簡單工廠模式的改進(或者稱為進一步的抽象)。

  将工廠抽象成兩層,AbsFactory(抽象工廠)和具體實作的工廠子類。程式員可以根據建立對象類型使用對應的工廠子類。這樣将單個的簡單工廠類變成了工廠簇,更利于代碼的維護和擴充。

  如果産品的種類很多的話,使用抽象工廠模式的靈活性會很高;如果産品的種類不多的話,使用簡單工廠模式就足夠了

  Pizza、BJChessPizza、BJPepperPizza、LDChessPizza、LDPepperPizza與工廠方法基本相同。

建立型模式之單例模式與工廠模式(一)
建立型模式之單例模式與工廠模式(一)