設計模式

1.設計模式簡介
  設計模式表明了最佳的實踐,一般被有經驗的面向對象的軟件開發人員所採用。設計模式是軟件開發人員在軟件開發過程當中面臨的通常問題的解決方案。這些解決方案是衆多軟件開發人員通過至關長的一段時間的試驗和錯誤總結出來的。
  設計模式是一套被反覆使用的、多數人知曉的、通過分類編目的、代碼設計經驗的總結。使用設計模式是爲了重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。毫無疑問,設計模式於己於他們於系統都是多贏的,設計模式使代碼編制真正工程化,設計模式是軟件工程的基石,如同大廈的一塊塊磚石同樣。項目中合理的運用設計模式能夠完美地解決不少問題,每種模式在現實中都有相應的原理來與之對應,每種模式都描述了一個在咱們周圍不斷重複發生的問題,以及該問題的核心解決方案,這也是設計模式能被普遍應用的緣由。
2.單例模式
單例模式(Singleton Pattern)是Java中最簡單的設計模式之一。這種類型的設計模式屬於建立型模式,它提供了一種建立對象的最佳方式。
這種模式涉及到一個單一的類,該類負責建立本身的對象,同時確保只有單個對象被建立。這個類提供了一種訪問其惟一的對象的方式,能夠直接訪問,不須要實例化該類的對象。
注意:1.單例類只能有一個實例。2.單例類必須本身建立本身的惟一實例。3.單例類必須給因此其餘對象提供一實例。
介紹
意圖:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
主要解決:一個全局使用的類頻繁地建立與銷燬。
什麼時候使用:當您想控制實例數目,節省系統資源的時候。
如何解決:判斷系統是否已經有這個單例,若是有就返回,若是沒有就建立。
關鍵代碼:構造函數是私有的。
應用實例:1.一個黨只能有一個主席。2.Windows是多進程多線程的,在操做一個文件的時候,就不可避免的出現多個進程或線程同時操做一個文件的現象,因此全部文件的處理必須經過惟一的實例來運行。3.一些設備管理器一般設計爲單例模式,好比一個電腦有兩臺打印機,在輸出的時候就要處理不能兩臺打印機打印同一個文件。
優勢:1.在內存裏只有一個實例,減小了內存的開銷,尤爲是頻繁的建立和銷燬實例(好比管理學院首頁頁面緩存)。2.避免對資源的多重佔用(好比寫文件操做)。
缺點:沒有接口,與單一職責原則衝突,一個類應該只關心內部邏輯,而不關心外面怎麼樣來實例化。
使用場景:1.要求生產惟一序列號。2.WEB中的計數器,不用每次刷新都在數據庫里加一次,用單例先緩存起來。3.建立的一個對象須要消耗的資源過多,好比IO與數據庫的鏈接等。
注意事項:getInstance()方法中須要使用同步鎖synchronized(Singleton.class)防止多線程同時進入形成instance被屢次實例化。
實現
咱們將建立一個SingleObject類。SingleObject類有它的私有構造函數和自己的一個靜態實例。
SingleObject類提供了一個靜態方法,供外界獲取它的靜態實例。SingletonPatternDemo,咱們的演示類使用SingletObject類來獲取SingleObject對象。
這裏寫圖片描述
步驟1:建立一個Singleton類。
SingletObject.javahtml

public class SingleObject {

   //建立 SingleObject 的一個對象
   private static SingleObject instance = new SingleObject();

   //讓構造函數爲 private,這樣該類就不會被實例化
   private SingleObject(){}

   //獲取惟一可用的對象
   public static SingleObject getInstance(){
      return instance;
   }

   public void showMessage(){
      System.out.println("Hello World!");
   }
}

步驟2:從singleton類獲取惟一的對象。
SingletonPatternDemo.javajava

public class SingletonPatternDemo {
   public static void main(String[] args) {

      //不合法的構造函數
      //編譯時錯誤:構造函數 SingleObject() 是不可見的
      //SingleObject object = new SingleObject();

      //獲取惟一可用的對象
      SingleObject object = SingleObject.getInstance();

      //顯示消息
      object.showMessage();
   }
}

步驟3:
驗證輸出。Hello World!數據庫

單例模式的幾種實現方式
單例模式的實現有多種方式,以下所示:
1.懶漢式,線程不安全
是否Lazy初始化:是
是否多線程安全:否
實現難度:易
描述:這種方式是最基本的實現方式,這種實現最大的問題就是不支持多線程,由於沒有加鎖synchronized,因此嚴格意義上它並不算單例模式。
這種方式lazy loading很明顯,不要求線程安全,在多線程不能正常工做。設計模式

public class Singleton{
    private static Singleton instance;
    private Singleton(){}
    public static Singleton getInstace(){
        if(instance==null){
            instance=new Singleton();
        }
        return instance;
    }
}

2.懶漢式,線程安全
是否lazy初始化:是
是否多線程安全:是
實現難度:易
描述:這種方式具有很好的lazy loading,可以在多線程中很好的工做,可是,效率低,99%狀況下不須要同步。
優勢:第一次調用才初始化,避免內存浪費。
缺點:必須加鎖synchronized才能保證單例,但加鎖會影響效率。
getInstance()的性能對應用程序不是很關鍵(該方法使用不頻繁)緩存

public class Singleton{
    private static Singleton instance;
    private singleton(){}
    public static synchronized Singleton getInstance(){
        if(instance==null){
            instance=new Singleton();
        }
        return instance;
    }
}

3.餓漢式
是否lazy初始化:否
是否多線程安全:是
實現難度:易
描述:這種方式比較經常使用,但容易產生垃圾對象。
優勢:沒有加鎖,執行效率會提升。
缺點:類加載時就初始化,浪費內存。
它基於classloader機制避免了多線程的同步問題,不過,instance在類裝載時就實例化,雖然致使類裝載的緣由有不少種,在單例模式中大多數都是調用getInstance方法,可是也不能肯定有其餘的方式(或者其餘的靜態方法)致使類裝載,這時候初始化instance顯然沒有達到lazy loading的效果。安全

public class Singleton{
    private static Singleton instance=new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){
        return instance;
    }   
}

4.雙檢鎖/雙重校驗鎖(DCL,即double-checked loading)
JDK版本:JDK1.5起
是否lazy初始化:是
是否多線程安全:是
實現難度:較複雜
描述:這種方式採用雙鎖機制,安全且在多線程狀況下能保持高性能。getInstance()的性能對應用程序很關鍵。服務器

public class Singleton{
    private volatile static Singleton singleton;
    private Singleton(){}
    public static Singleton getInstance(){
        if(singleton==null){
            synchronized(Singleton.class){
            if(singleton==null){
                singleton=new Singleton();
                }
            }
        }
        return singleton;
    }
}

5.登記式/靜態內部類
是否lazy初始化:是
是否多線程安全:是
實現難度:通常
描述:這種方式能達到雙檢鎖方式同樣的功效,但實現更簡單。對靜態域使用延遲初始化,應使用這種方式而不是雙檢鎖方式。這種方式只適用於靜態域的狀況,雙檢鎖方式可在實例域須要延遲初始化時使用。
這種方式一樣利用了 classloder 機制來保證初始化 instance 時只有一個線程,它跟第 3 種方式不一樣的是:第 3 種方式只要 Singleton 類被裝載了,那麼 instance 就會被實例化(沒有達到 lazy loading 效果),而這種方式是 Singleton 類被裝載了,instance 不必定被初始化。由於 SingletonHolder 類沒有被主動使用,只有顯示經過調用 getInstance 方法時,纔會顯示裝載 SingletonHolder 類,從而實例化 instance。想象一下,若是實例化instance 很消耗資源,因此想讓它延遲加載,另一方面,又不但願在 Singleton 類加載時就實例化,由於不能確保 Singleton 類還可能在其餘的地方被主動使用從而被加載,那麼這個時候實例化 instance 顯然是不合適的。這個時候,這種方式相比第 3 種方式就顯得很合理。多線程

public class Singleton{
    private static class SingletonHolder{
    private static final Singleton INSTSNCE=new Singleton();
    }
    private Singleton(){}
    private static final Singleton getInstance(){
    return SingletonHolder.INSTANCE;
    }
}

六、枚舉
JDK 版本:JDK1.5 起
是否 Lazy 初始化:否
是否多線程安全:是
實現難度:易
描述:這種實現方式尚未被普遍採用,但這是實現單例模式的最佳方法。它更簡潔,自動支持序列化機制,絕對防止屢次實例化。
這種方式是 Effective Java 做者 Josh Bloch 提倡的方式,它不只能避免多線程同步問題,並且還自動支持序列化機制,防止反序列化從新建立新的對象,絕對防止屢次實例化。不過,因爲 JDK1.5 以後才加入 enum 特性,用這種方式寫難免讓人感受生疏,在實際工做中,也不多用。
不能經過 reflection attack 來調用私有構造方法。 框架

public enum Singleton{
    INSTANCE;
    public void whateverMethod(){
    }
}

經驗之談:通常狀況下,不建議使用第 1 種和第 2 種懶漢方式,建議使用第 3 種餓漢方式。只有在要明確實現 lazy loading 效果時,纔會使用第 5 種登記方式。若是涉及到反序列化建立對象時,能夠嘗試使用第 6 種枚舉方式。若是有其餘特殊的需求,能夠考慮使用第 4 種雙檢鎖方式。ide

3.工廠模式
  工廠模式(Factory Pattern)是Java中最經常使用的設計模式之一。這類模型的設計模式屬於建立型模式,它提供了一種建立對象的最佳方式。
  在工廠模式中,咱們在建立對象時不會對客戶端暴露建立邏輯,而且是經過使用一個共同的接口來指向新建立的對象。
  介紹
  意圖:定義了一個建立對象的接口,讓其子類本身決定實例化哪個工廠類,工廠模式使其建立過程延遲到子類進行。
  主要解決:主要解決接口選擇的問題。
  什麼時候使用:咱們明確的計劃不一樣條件下建立不一樣實例時。
  如何解決:讓其子類實現工廠接口,返回的也是一個抽象的產品。
  關鍵代碼:建立過程在其子類執行。
  應用實例:1.您須要一輛汽車,能夠直接從工廠裏提貨,而不用去管這輛汽車是怎麼作出來的,以及這個汽車裏面的具體實現。2.Hibernate換數據庫只需換方言和驅動就能夠。
  優勢:1.一個調用者想建立一個對象,只要知道其名稱就能夠了。2.擴展性高,若是想增長一個產品,只要擴展一個工廠類就能夠。3.屏蔽產品的具體實現,調用者只關心產品的接口。
  缺點:每次增長一個產品時,都須要增長一個具體類和對象實現工廠,使得系統中類的個數成倍增長,在必定程度上增長了系統的複雜度,同時也增長了系統具體類的依賴,這並非什麼好事。
  使用場景:1.日誌記錄器:記錄可能記錄到本地硬盤、系統事件、遠程服務器等,用戶能夠選擇記錄日誌到什麼地方。2.數據庫訪問,當用戶不知道最好系統採用哪一類數據庫,以及數據庫可能有變化時。3.設計一個鏈接服務器的框架,須要三個協議,「POP3」、「IMAP」、「HTTP」,能夠把這三個做爲產品類,共同實現一個藉口。
  注意事項:做爲一種建立類模式,在任何須要生成複雜對象的地方,均可以使用工廠方法模式。有一點須要注意的地方就是複雜對象適合使用工廠模式,而簡單對象,特別是須要經過new就能夠完成的對象,無需使用工廠模式。若是使用工廠模式,就須要引入一個工廠類,會增長系統的複雜度。
  實現
  咱們將建立一個shape接口和實現shape接口的實體類,下一步是定義工廠類ShapeFactory。FactoryPatternDemo,咱們的演示類使用shapeFactory來獲取Shape對象。它將向ShapeFactory傳遞信息(CIRCLE/RECTANGLE/SQUARE),以便獲取它所需對象的類型。
  這裏寫圖片描述
  步驟1:
  建立一個接口。
  Shape.java

public interface Shape{
void draw();
}

步驟2:
建立實現接口的實體類。
Rectangle.java

public class Rectangle implements Shape{
    @override
    public void draw(){
        System.out.println("Inside Rectangle::draw() method.");
    }
}

Square.java

public class Square implements Shape{
    @overrride
    public void draw(){
        System.out.println("Inside Square::draw() method.");
    }
}

Circle.java

public class Circle implements Shape{
    @override
    public void draw(){
        System.out.println("Inside Circle::draw() method.");
    }
}

步驟3:
建立一個工廠,生產基於給定信息的實體類的對象。
ShapeFactory.class

public class shapeFactory{
    public shape getShape(String shapeType){
        if(shapeType==null){
            return null;
        }
        if(shapeType.equalsIgnoreCase("CIRCLE")){
            return new Circle();
        }
        if(shapeType.equalsIgnoreCase("RECTANGLE")){
            return new Rectangle();
        }
        if(shapeType.equalsIgnoreCase("SQUARE")){
            return new Square();
        }
        return  null;
    }
}

步驟4
使用該工廠,經過傳遞類型信息來獲取實體類的對象。
FactoryPatternDemo.java

public class FactoryPatternDemo{
    public static void main(String[] args){
        ShapeFactory shapeFactory=new ShapeFactory();
        Shape shape1=shapeFactory.getShape("CIRCLE");
        shape1.draw();
        Shape shape2=shapeFactory.getShape("RECTANGLE");
        shape2.draw();
        Shape shape3=shapeFactory.getShape("SQUARE");
        shape3.draw();      
    }
}

轉自:http://www.runoob.com/design-pattern/design-pattern-tutorial.html

相關文章
相關標籤/搜索