設計模式之創(chuàng)建型模式

下面總結設計模式中的創(chuàng)建型模式:

1.簡單工廠模式

簡單工廠不是設計模式,更像是一種編程習慣。它把實例化的操作單獨放到一個類中,這個類就成為簡單工廠類,讓簡單工廠類來決定應該用哪個具體子類來實例化券敌。
在簡單工廠模式中,我們在創(chuàng)建對象時不會對客戶端暴露創(chuàng)建邏輯柳洋,并且是通過使用一個共同的接口來指向新創(chuàng)建的對象待诅,從而將客戶類和具體子類的實現(xiàn)解耦,客戶類不再需要知道有哪些子類以及應當實例化哪個子類熊镣”把悖客戶類往往有多個立由,如果不使用簡單工廠,那么所有的客戶類都要知道所有子類的細節(jié)序厉。而且一旦子類發(fā)生改變锐膜,例如增加子類,那么所有的客戶類都要進行修改弛房。

介紹

意圖:在創(chuàng)建一個對象時不向客戶暴露內部細節(jié)道盏,并提供一個創(chuàng)建對象的通用接口。

主要解決:主要解決接口選擇的問題文捶。

何時使用:我們明確地計劃不同條件下創(chuàng)建不同實例時荷逞。

如何解決:讓其子類實現(xiàn)工廠接口,返回的也是一個抽象的產品粹排。

關鍵代碼:創(chuàng)建過程在其子類執(zhí)行种远。

應用實例: 1、您需要一輛汽車顽耳,可以直接從工廠里面提貨坠敷,而不用去管這輛汽車是怎么做出來的,以及這個汽車里面的具體實現(xiàn)射富。 2膝迎、Hibernate 換數(shù)據(jù)庫只需換方言和驅動就可以。

優(yōu)點: 1胰耗、一個調用者想創(chuàng)建一個對象限次,只要知道其名稱就可以了。 2柴灯、擴展性高卖漫,如果想增加一個產品,只要擴展一個工廠類就可以赠群。 3羊始、屏蔽產品的具體實現(xiàn),調用者只關心產品的接口乎串。

缺點:每次增加一個產品時店枣,都需要增加一個具體類和對象實現(xiàn)工廠速警,使得系統(tǒng)中類的個數(shù)成倍增加叹誉,在一定程度上增加了系統(tǒng)的復雜度,同時也增加了系統(tǒng)具體類的依賴闷旧。這并不是什么好事长豁。

使用場景: 1、日志記錄器:記錄可能記錄到本地硬盤忙灼、系統(tǒng)事件匠襟、遠程服務器等钝侠,用戶可以選擇記錄日志到什么地方。 2酸舍、數(shù)據(jù)庫訪問帅韧,當用戶不知道最后系統(tǒng)采用哪一類數(shù)據(jù)庫,以及數(shù)據(jù)庫可能有變化時啃勉。 3忽舟、設計一個連接服務器的框架,需要三個協(xié)議淮阐,"POP3"叮阅、"IMAP"、"HTTP"泣特,可以把這三個作為產品類浩姥,共同實現(xiàn)一個接口。

注意事項:作為一種創(chuàng)建類模式状您,在任何需要生成復雜對象的地方勒叠,都可以使用工廠模式。有一點需要注意的地方就是復雜對象適合使用工廠模式膏孟,而簡單對象缴饭,特別是只需要通過 new 就可以完成創(chuàng)建的對象,無需使用工廠模式骆莹。如果使用工廠模式颗搂,就需要引入一個工廠類,會增加系統(tǒng)的復雜度幕垦。

實現(xiàn)

我們將創(chuàng)建一個 Shape 接口和實現(xiàn) Shape 接口的實體類丢氢。下一步是定義工廠類 ShapeFactory。
FactoryPatternDemo先改,我們的演示類使用 ShapeFactory 來獲取 Shape 對象疚察。它將向 ShapeFactory 傳遞信息(CIRCLE / RECTANGLE / SQUARE),以便獲取它所需對象的類型仇奶。

簡單工廠模式.jpg

步驟 1
創(chuàng)建一個接口貌嫡。
Shape.java

public interface Shape {
   void draw();
}

步驟 2
創(chuàng)建實現(xiàn)接口的實體類。
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 {

   @Override
   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
創(chuàng)建一個工廠该溯,生成基于給定信息的實體類的對象岛抄。
ShapeFactory.java

public class ShapeFactory {
    
   /*使用 getShape 方法獲取形狀類型的對象,
   在實際操作中建議把CIRCLE等定義為常量*/
   public static Shape getShape(String shapeType){
      if(shapeType == null){
         return null;
      }        
      if(shapeType.equalsIgnoreCase("CIRCLE")){
         return new Circle();
      } else if(shapeType.equalsIgnoreCase("RECTANGLE")){
         return new Rectangle();
      } else if(shapeType.equalsIgnoreCase("SQUARE")){
         return new Square();
      }
      return null;
   }
}

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

public class FactoryPatternDemo {

   public static void main(String[] args) {
      

      //獲取 Circle 的對象,并調用它的 draw 方法
      Shape shape1 = ShapeFactory.getShape("CIRCLE");

      //調用 Circle 的 draw 方法
      shape1.draw();

      //獲取 Rectangle 的對象氯庆,并調用它的 draw 方法
      Shape shape2 = ShapeFactory.getShape("RECTANGLE");

      //調用 Rectangle 的 draw 方法
      shape2.draw();

      //獲取 Square 的對象蹭秋,并調用它的 draw 方法
      Shape shape3 = ShapeFactory.getShape("SQUARE");

      //調用 Square 的 draw 方法
      shape3.draw();
   }
}

步驟 5
驗證輸出扰付。
Inside Circle::draw() method.
Inside Rectangle::draw() method.
Inside Square::draw() method.

使用反射機制可以解決每次增加一個產品時,都需要增加一個對象實現(xiàn)工廠接口的缺點仁讨。注意要使用完整包名羽莺,也可以使用Properties文件做映射。

public class ShapeFactory {
    public static Shape getShapeByClassName(String className) {
    Shape obj = null;

        try {
            obj = (Shape)Class.forName(clazz.getName()).newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return obj;
        
    }
}

2.工廠方法模式

介紹

意圖:定義了一個創(chuàng)建對象的接口洞豁,但由子類決定要實例化哪個類禽翼。工廠方法把實例化操作推遲到子類。

工廠方法和簡單工廠的區(qū)別:簡單工廠模式的實質是由一個工廠類根據(jù)傳入的參數(shù)族跛,動態(tài)決定應該創(chuàng)建哪一個產品類(這些產品類繼承自一個父類或接口)的實例闰挡。相當于是一個工廠中有各種產品,這樣工廠的職責較重礁哄,而且當產品類型過多時不利于系統(tǒng)的擴展維護长酗。如果增加一類產品,必須修改工廠類的方法桐绒。而工廠方法提供了一個工廠接口夺脾,定義了許多工廠實現(xiàn)類來負責生產不同的產品,這樣如果想要增加一類產品只需定義一個新的工廠實現(xiàn)類即可茉继。且簡單工廠返回產品的方法為static的咧叭,因為簡單工廠不需要創(chuàng)建工廠實例,而工廠方法要創(chuàng)建工廠實例烁竭。

工廠模式的實際應用:

JDBC中Connection的創(chuàng)建
特定的Driver創(chuàng)建對應的connection菲茬。

Spring的Bean管理

實現(xiàn)

在簡單工廠中,創(chuàng)建對象的是另一個類派撕,而在工廠方法中婉弹,是由子類來創(chuàng)建對象。
下圖中终吼,F(xiàn)actory 有一個 doSomething() 方法镀赌,這個方法需要用到一個產品對象,這個產品對象由 factoryMethod() 方法創(chuàng)建际跪。該方法是抽象的商佛,需要由子類去實現(xiàn)。


Factory.java

public abstract class Factory {
    abstract public Product factoryMethod();
    public void doSomething() {
        Product product = factoryMethod();
        // do something with the product
    }
}

ConcreteFactory.java

public class ConcreteFactory extends Factory {
    public Product factoryMethod() {
        return new ConcreteProduct();
    }
}

BrickFactory.java

public class BrickFactory extends Factory {
    public Product factoryMethod() {
        return new Brick();
    }
}

SteelFactory.java

public class SteelFactory extends Factory {
    public Product factoryMethod() {
        return new Steel();
    }
}

3.抽象工廠模式

抽象工廠模式(Abstract Factory Pattern)是圍繞一個超級工廠創(chuàng)建其他工廠姆打。該超級工廠又稱為其他工廠的工廠良姆。這種類型的設計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式穴肘。

在抽象工廠模式中歇盼,接口是負責創(chuàng)建一個相關對象的工廠舔痕,不需要顯式指定它們的類评抚。每個生成的工廠都能按照工廠模式提供對象豹缀。

抽象工廠模式是所有形態(tài)的工廠模式中最為抽象和最具一般性的一種形態(tài)。

抽象工廠模式創(chuàng)建的是對象家族慨代,也就是很多對象而不是一個對象邢笙,并且這些對象是相關的,也就是說必須一起創(chuàng)建出來侍匙。而工廠方法模式只是用于創(chuàng)建一個對象氮惯,這和抽象工廠模式有很大不同。

為了方便引進抽象工廠模式想暗,引進一個新概念:產品族(Product Family)妇汗。所謂產品族,是指位于不同產品等級結構说莫,功能相關聯(lián)的產品組成的家族杨箭。如圖:

產品族1.gif

圖中一共有四個產品族,分布于三個不同的產品等級結構中储狭。同一個產品族是同一個工廠生產的互婿,而不同等級結構來自不同的工廠。只要指明一個產品所處的產品族以及它所屬的等級結構辽狈,就可以唯一的確定這個產品慈参。
所謂的抽象工廠是指一個工廠等級結構可以創(chuàng)建出分屬于不同產品等級結構的一個產品族中的所有對象。如果用圖來描述的話刮萌,如下圖:

產品族2.gif

介紹

意圖:提供一個接口驮配,用于創(chuàng)建相關的對象家族

主要解決:主要解決接口選擇的問題着茸。

何時使用:系統(tǒng)的產品有多于一個的產品族僧凤,而系統(tǒng)只消費其中某一族的產品。

如何解決:在一個產品族里面元扔,定義多個產品躯保。

關鍵代碼:在一個工廠里聚合多個同類產品。

應用實例:工作了澎语,為了參加一些聚會途事,肯定有兩套或多套衣服吧,比如說有商務裝(成套擅羞,一系列具體產品)尸变、時尚裝(成套,一系列具體產品)减俏,甚至對于一個家庭來說召烂,可能有商務女裝、商務男裝娃承、時尚女裝奏夫、時尚男裝怕篷,這些也都是成套的法梯,即一系列具體產品遭殉。假設一種情況(現(xiàn)實中是不存在的,要不然彩扔,沒法進入共產主義了麻削,但有利于說明抽象工廠模式)蒸痹,在您的家中,某一個衣柜(具體工廠)只能存放某一種這樣的衣服(成套呛哟,一系列具體產品)叠荠,每次拿這種成套的衣服時也自然要從這個衣柜中取出了。用 OO 的思想去理解扫责,所有的衣柜(具體工廠)都是衣柜類的(抽象工廠)某一個蝙叛,而每一件成套的衣服又包括具體的上衣(某一具體產品),褲子(某一具體產品)公给,這些具體的上衣其實也都是上衣(抽象產品)借帘,具體的褲子也都是褲子(另一個抽象產品)。

優(yōu)點:當一個產品族中的多個對象被設計成一起工作時淌铐,它能保證客戶端始終只使用同一個產品族中的對象肺然。

缺點:產品族擴展非常困難,要增加一個系列的某一產品腿准,既要在抽象的 Creator 里加代碼际起,又要在具體的里面加代碼。

使用場景: 1吐葱、QQ 換皮膚街望,一整套一起換。 2弟跑、生成不同操作系統(tǒng)的程序灾前。

注意事項:產品族難擴展,產品等級易擴展孟辑。

抽象工廠模式.gif

上圖的描述用產品族描述如下:

產品族3.gif

實現(xiàn)

我們來舉這樣一個例子哎甲,QQ秀有不同的裝扮,分為男孩和女孩饲嗽,而男孩又分為圣誕男孩和新年男孩炭玫,女孩分為圣誕女孩和新年女孩。那么就可以有一個抽象工廠生產男孩和女孩貌虾。兩個具體的工廠分別生產圣誕系列的男孩和女孩吞加、新年系列的男孩和女孩。同一系列的男孩和女孩是一個產品族,而不同系列構成不同的產品等級衔憨。

步驟 1
為男孩創(chuàng)建一個接口叶圃。
Boy.java

public interface Boy {
    public void drawMan();
}

步驟 2
創(chuàng)建實現(xiàn)接口的實體類。
MCBoy.java

public class MCBoy implements Boy {

    @Override
    public void drawMan() {
        System.out.println("-----------------圣誕系列的男孩子--------------------");
    }

}

HNBoy.java

public class HNBoy implements Boy {

    @Override
    public void drawMan() {
        System.out.println("-----------------新年系列的男孩子--------------------");
    }

}

步驟 3
為女孩創(chuàng)建一個接口
Girl.java

public interface Girl {
    public void drawWomen();
}

步驟 4
創(chuàng)建實現(xiàn)接口的實體類巫财。
MCGirl.java

public class MCGirl implements Girl {

    @Override
    public void drawWomen() {
        System.out.println("-----------------圣誕系列的女孩子--------------------");
    }

}

HNGirl.java

public class HNGirl implements Girl {

    @Override
    public void drawWomen() {
        // TODO Auto-generated method stub
        System.out.println("-----------------新年系列的女孩子--------------------");
    }

}

步驟 5
創(chuàng)建生產男孩女孩的抽象工廠接口
PersonFactory.java

public interface PersonFactory {
    //男孩接口
    public Boy getBoy();
    //女孩接口
    public Girl getGirl();  
}

步驟 6
創(chuàng)建生產圣誕和新年系列的具體工廠
MCFactory.java

public class MCFctory implements PersonFactory {

    @Override
    public Boy getBoy() {
        return new MCBoy();
    }

    @Override
    public Girl getGirl() {
        return new MCGirl();
    }

}

HNFactory.java

public class HNFactory implements PersonFactory {

    @Override
    public Boy getBoy() {
        return new HNBoy();
    }

    @Override
    public Girl getGirl() {
        return new HNGirl();
    }

}

步驟 7
使用工廠生產
AbstractFactoryPatternDemo.java

public class AbstractFactoryPatternDemo{
    public static void main(String[] args){
        MCFactory mcFactory = new MCFactory();
        HNFactory hnFactory = new HNFactory();
        Boy mcBoy = mcFactory.getBoy();
        Girl mcGirl = mcFactory.getGirl();
        Boy hnBoy = hnFactory.getBoy();
        Girl hnGirl = hnFactory.getGirl();
        mcBoy.drawMan();
        mcGirl.drawWomen();
        hnBoy.drawMan();
        hnGirl.drawWomen();
    }
}

步驟 8
驗證輸出
-----------------圣誕系列的男孩子--------------------
-----------------圣誕系列的女孩子--------------------
-----------------新年系列的男孩子--------------------
-----------------新年系列的女孩子--------------------

抽象工廠模式用到了工廠方法模式來創(chuàng)建單一對象盗似,PersonFactory 中的 getBoy() 和 getGirl() 方法都是讓子類來實現(xiàn)哩陕,這兩個方法單獨來看就是在創(chuàng)建一個對象平项,這符合工廠方法模式的定義。

至于創(chuàng)建對象的家族這一概念是在 AbstractFactoryPatternDemo 體現(xiàn)悍及, AbstractFactoryPatternDemo 要通過 PersonFactory 同時調用兩個方法來創(chuàng)建出兩個對象闽瓢,在這里這兩個對象就有很大的相關性, AbstractFactoryPatternDemo 需要同時創(chuàng)建出這兩個對象心赶。

從高層次來看扣讼,抽象工廠使用了組合,即 AbstractFactoryPatternDemo 組合了 PersonFactory缨叫,而工廠方法模式使用了繼承椭符。

注意:抽象工廠也可以運用反射,只不過反射的不再是產品類耻姥,而是不同的具體工廠類销钝。

對比.png

4.單例模式

單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種類型的設計模式屬于創(chuàng)建型模式琐簇,它提供了一種創(chuàng)建對象的最佳方式蒸健。
這種模式涉及到一個單一的類,該類負責創(chuàng)建自己的對象婉商,同時確保只有單個對象被創(chuàng)建似忧。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問丈秩,不需要實例化該類的對象盯捌。
注意:
1、單例類只能有一個實例蘑秽。
2挽唉、單例類必須自己創(chuàng)建自己的唯一實例,而不是在外部隨意地new對象筷狼。
3瓶籽、單例類必須給所有其他對象提供這一實例。

介紹

意圖:保證一個類僅有一個實例埂材,并提供一個訪問它的全局訪問點塑顺。
主要解決:一個全局使用的類頻繁地創(chuàng)建與銷毀。
何時使用:當想控制實例數(shù)目,節(jié)省系統(tǒng)資源的時候严拒。
如何解決:判斷系統(tǒng)是否已經有這個單例扬绪,如果有則返回,如果沒有則創(chuàng)建裤唠。
關鍵代碼:使用一個私有構造函數(shù)挤牛、一個私有靜態(tài)變量以及一個公有靜態(tài)函數(shù)來實現(xiàn)。私有構造函數(shù)保證了不能通過構造函數(shù)來創(chuàng)建對象實例种蘸,只能通過公有靜態(tài)函數(shù)返回唯一的私有靜態(tài)變量墓赴。
應用實例: 1、一個黨只能有一個主席航瞭。 2诫硕、Windows 是多進程多線程的,在操作一個文件的時候刊侯,就不可避免地出現(xiàn)多個進程或線程同時操作一個文件的現(xiàn)象章办,所以所有文件的處理必須通過唯一的實例來進行。 3滨彻、一些設備管理器常常設計為單例模式藕届,比如一個電腦有兩臺打印機,在輸出的時候就要處理不能兩臺打印機打印同一個文件亭饵。
優(yōu)點: 1休偶、在內存里只有一個實例,減少了內存的開銷冬骚。2椅贱、避免頻繁的創(chuàng)建和銷毀實例,提高性能(比如管理學院首頁頁面緩存)只冻。 2庇麦、避免對資源的多重占用(比如寫文件操作)。
缺點:1喜德、擴展比較困難山橄,沒有接口,不能繼承舍悯,與單一職責原則沖突航棱,一個類應該只關心內部邏輯,而不關心外面怎么樣來實例化萌衬。2饮醇、如果實例化后的對象長期不利用,系統(tǒng)將默認為垃圾進行回收秕豫,造成對象狀態(tài)丟失朴艰。
使用場景: 1观蓄、當多個實例存在可能引起程序邏輯錯誤,如要求生產唯一序列號祠墅。 2侮穿、對系統(tǒng)內資源要求統(tǒng)一讀寫,如讀寫配置信息毁嗦,又如WEB 中的計數(shù)器亲茅,不用每次刷新都在數(shù)據(jù)庫里加一次,用單例先緩存起來狗准。 3克锣、創(chuàng)建的一個對象需要消耗的資源過多,但同時又需要用到該對象驶俊,比如 I/O 與數(shù)據(jù)庫的連接等娶耍。
注意事項:getInstance() 方法中需要使用同步鎖 synchronized (Singleton.class) 防止多線程同時進入造成 instance 被多次實例化免姿。

實現(xiàn)

我們將創(chuàng)建一個 SingleObject 類饼酿。SingleObject 類有它的私有構造函數(shù)和本身的一個靜態(tài)實例。
SingleObject 類提供了一個靜態(tài)方法胚膊,供外界獲取它的靜態(tài)實例故俐。SingletonPatternDemo,我們的演示類使用 SingleObject 類來獲取 SingleObject 對象紊婉。

單例模式.jpg

步驟 1
創(chuàng)建一個 Singleton 類药版。

SingleObject.java
public class SingleObject {

   //創(chuàng)建 SingleObject 的一個對象
   private static SingleObject instance = new SingleObject();

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

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

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

步驟 2
從 singleton 類獲取唯一的對象喻犁。

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

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

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

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

步驟 3
驗證輸出槽片。

Hello World!

單例模式的幾種實現(xiàn)方式

單例模式的實現(xiàn)有多種方式,如下所示:
1肢础、懶漢式还栓,線程不安全
是否 Lazy 初始化:
是否多線程安全:
實現(xiàn)難度:
代碼實例:

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
  
    public static Singleton getInstance() {  
    if (instance == null) {  /*懶漢式標志:Lazy 初始化,
    在外部第一次請求使用該類對象時才實例化,是時間換空間的模式*/
        instance = new Singleton();  
    }  
    return instance;  
    }  
}  

描述:這種方式是最基本的實現(xiàn)方式传轰,這種實現(xiàn)最大的問題就是不支持多線程剩盒。假設開始線程1進入,判斷instance為空慨蛙,在將要創(chuàng)建實例時辽聊,時間片切換,線程2又進來了期贫,同樣判斷instance為空跟匆,創(chuàng)建了實例,這是CPU調度回到線程1通砍,繼續(xù)創(chuàng)建實例玛臂。因為沒有加鎖 synchronized,所以嚴格意義上它并不算單例模式。
這種方式 lazy loading 很明顯垢揩,不要求線程安全玖绿,在多線程不能正常工作。
接下來介紹的幾種實現(xiàn)方式都支持多線程叁巨,但是在性能上有所差異斑匪。

2、懶漢式锋勺,線程安全
是否 Lazy 初始化:
是否多線程安全:
實現(xiàn)難度:
代碼實例:

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  
    //加同步鎖
    if (instance == null) {   
        instance = new Singleton();  
    }  
    return instance;  
    }  
} 

描述:這種方式具備很好的 lazy loading蚀瘸,能夠在多線程中很好的工作,但是庶橱,效率很低贮勃,99% 情況下不需要同步。
優(yōu)點:第一次調用才初始化苏章,避免內存浪費寂嘉。
缺點:必須加鎖 synchronized 才能保證單例,但加鎖會影響效率枫绅,下一個線程想要取得對象泉孩,必須等上一個線程釋放鎖之后,才可以繼續(xù)執(zhí)行并淋。
getInstance() 的性能對應用程序不是很關鍵(該方法使用不太頻繁)寓搬。

3、餓漢式
是否 Lazy 初始化:
是否多線程安全:
實現(xiàn)難度:
代碼實例:

public class Singleton {  
    private static Singleton instance = new Singleton();  
                           /*餓漢式標志:在類加載時直接初始化县耽,
                           是空間換時間的模式*/
    private Singleton (){}  
    public static Singleton getInstance() {  
    return instance;  
    }  
}  

描述:這種方式比較常用句喷,但容易產生垃圾對象。
優(yōu)點:沒有加鎖兔毙,執(zhí)行效率會提高唾琼。
缺點:類加載時就初始化,浪費內存瞒御。
它基于 classloder 機制避免了多線程的同步問題父叙,不過,instance 在類裝載時就實例化肴裙,雖然導致類裝載的原因有很多種趾唱,在單例模式中大多數(shù)都是調用 getInstance 方法, 但是也不能確定有其他的方式(或者其他的靜態(tài)方法)導致類裝載蜻懦,這時候初始化 instance 顯然沒有達到 lazy loading 的效果甜癞。

4、靜態(tài)代碼塊
是否 Lazy 初始化:
是否多線程安全:
實現(xiàn)難度:
代碼實例:

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

描述:類似于餓漢式宛乃。
優(yōu)點:沒有加鎖悠咱,執(zhí)行效率會提高蒸辆。
缺點:類加載時就初始化,浪費內存析既。

5躬贡、雙檢鎖/雙重校驗鎖(DCL,即 double-checked locking)
JDK 版本:JDK1.5 起
是否 Lazy 初始化:
是否多線程安全:
實現(xiàn)難度:較復雜
代碼實例:

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

描述:這種方式采用雙鎖機制眼坏,安全且在多線程情況下能保持高性能拂玻。getInstance() 的性能對應用程序很關鍵。這種方法既能保證線程安全又能提高了效率宰译。
假設線程1進入方法檐蚜,instance為空,進入同步代碼塊沿侈,時間片切換闯第,線程2進來,instance為空缀拭,在同步代碼塊外被阻塞咳短,因為此時線程1正在里面。cup切換智厌,線程1執(zhí)行創(chuàng)建實例诲泌,當2再進入代碼塊后盲赊,此時instace不為空铣鹏,直接返回instance。當再有線程進來哀蘑,instance不為空诚卸,不用執(zhí)行同步代碼塊,提高了效率绘迁。
外層 if 語句是為了保證 instance 不為空時不執(zhí)行同步代碼塊合溺,直接返回對象,提高效率缀台;里層 if 語句則是為了防止之前已經進入外層 if 語句的線程重復實例化對象棠赛,保證單例。

注意:singleton 采用 volatile 關鍵字修飾是很有必要的膛腐!
這里涉及到了JVM編譯器的指令重排睛约。
簡單的一句 singleton = new Singleton(); 會被編譯器編譯成如下JVM指令:

memory =allocate(); //1:分配對象的內存空間
ctorInstance(memory); //2:初始化對象
instance =memory; //3:設置instance指向剛分配的內存地址

但是這些指令順序并非一成不變,有可能會經過JVM和CPU的優(yōu)化哲身,指令重排成下面的順序:
memory =allocate(); //1:分配對象的內存空間
instance =memory; //3:設置instance指向剛分配的內存地址
ctorInstance(memory); //2:初始化對象

當線程A執(zhí)行完1,3,時辩涝,instance對象還未完成初始化,但已經不再指向null勘天。此時如果線程B搶占到CPU資源怔揩,執(zhí)行 if(instance == null)的結果會是false捉邢,從而返回一個沒有初始化完成的instance對象。如下圖所示:


使用 volatile 可以禁止 JVM 的指令重排商膊,保證在多線程環(huán)境下也能正常運行伏伐。

6、登記式 / 靜態(tài)內部類
是否 Lazy 初始化:
是否多線程安全:
實現(xiàn)難度:一般
代碼實例:

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

描述:這里有幾個需要注意的點:
1晕拆、從外部無法訪問靜態(tài)內部類LazyHolder秘案,只有當調用Singleton.getInstance()方法的時候,才能得到單例對象INSTANCE潦匈。
2阱高、靜態(tài)內部類LazyHolder是懶加載的,它不隨外部類Singleton的加載而加載茬缩。所以INSTANCE對象不會在單例類Singleton被加載的時候就初始化赤惊,而是在調用getInstance()方法時,靜態(tài)內部類LazyHolder被加載的時候被初始化凰锡。因此這種實現(xiàn)方式是利用classloader的加載機制來實現(xiàn)懶加載未舟,并保證構建單例的線程安全。

7掂为、序列化與反序列化
是否 Lazy 初始化:
是否多線程安全:
實現(xiàn)難度:一般
代碼實例:
MyObject.java


SaveAndRead.java


運行結果

發(fā)現(xiàn)返回的不是同一個對象

去掉如下代碼的注釋


運行結果

返回的是同一個對象

分析
靜態(tài)內部類可以實現(xiàn)線程安全裕膀,但如果遇到序列化對象,使用默認的方法運行得到的結果還是多例的勇哗。如果既想要做到可序列化昼扛,又想要反序列化為同一對象,則必須實現(xiàn)readResolve方法欲诺。readResolve() 方法會緊挨著 readObject() 之后被調用抄谐,該方法的返回值將會代替原來反序列化的對象,而原來 readObject() 反序列化的對象將會被立即丟棄扰法。

8蛹含、枚舉
JDK 版本:JDK1.5 起
是否 Lazy 初始化:
是否多線程安全:
實現(xiàn)難度:
代碼實例:

public enum Singleton {
    INSTANCE;
}

描述:單元素的枚舉類型是實現(xiàn)單例模式的最佳方法。它更簡潔塞颁,自動支持序列化機制浦箱,絕對防止多次實例化。
這種方式是 Effective Java 作者 Josh Bloch 提倡的方式祠锣,它不僅能避免多線程同步問題酷窥,而且還自動支持序列化機制,該實現(xiàn)在多次序列化再進行反序列化之后锤岸,不會得到多個實例竖幔。而其它實現(xiàn),為了保證不會出現(xiàn)反序列化之后出現(xiàn)多個實例是偷,需要使用 transient 修飾所有字段拳氢,并且實現(xiàn)序列化和反序列化的方法募逞,來防止反序列化重新創(chuàng)建新的對象。不過馋评,由于 JDK1.5 之后才加入 enum 特性放接,用這種方式寫不免讓人感覺生疏,在實際工作中留特,也很少用纠脾。

該實現(xiàn)可以防止反射攻擊。在其它實現(xiàn)中蜕青,通過 setAccessible() 方法可以將私有構造函數(shù)的訪問級別設置為 public苟蹈,然后調用構造函數(shù)從而實例化對象,如果要防止這種攻擊右核,需要在構造函數(shù)中添加防止實例化第二個對象的代碼慧脱。但是該實現(xiàn)是由 JVM 保證只會實例化一次,因此不會出現(xiàn)上述的反射攻擊贺喝。

經驗之談:一般情況下菱鸥,不建議使用第 1 種和第 2 種懶漢方式,建議使用第 3 種餓漢方式躏鱼。只有在要明確實現(xiàn) lazy loading 效果時氮采,才會使用第 6 種登記方式。如果涉及到反序列化創(chuàng)建對象時染苛,可以嘗試使用第 8 種枚舉方式鹊漠。如果有其他特殊的需求,可以考慮使用第 5 種雙檢鎖方式殖侵。

5.建造者模式

建造者模式(Builder Pattern)使用多個簡單的對象一步一步構建成一個復雜的對象贸呢。這種類型的設計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式拢军。
一個 Builder 類會一步一步構造最終的對象。該 Builder 類是獨立于其他對象的怔鳖。

介紹

意圖:將一個復雜的構建與其表示相分離茉唉,使得同樣的構建過程可以創(chuàng)建不同的表示。
主要解決:主要解決在軟件系統(tǒng)中结执,有時候面臨著"一個復雜對象"的創(chuàng)建工作度陆,其通常由各個部分的子對象用一定的算法構成;由于需求的變化献幔,這個復雜對象的各個部分經常面臨著劇烈的變化懂傀,但是將它們組合在一起的算法卻相對穩(wěn)定。
何時使用:一些基本部件不會變蜡感,而其組合經常變化的時候蹬蚁。
如何解決:將變與不變分離開恃泪。
關鍵代碼:建造者:創(chuàng)建和提供實例,導演:管理建造出來的實例的依賴關系犀斋。
應用實例:
1贝乎、去肯德基,漢堡叽粹、可樂览效、薯條、炸雞翅等是不變的虫几,而其組合是經常變化的锤灿,生成出所謂的"套餐"。
2辆脸、JAVA 中的 StringBuilder衡招。
優(yōu)點:1、建造者獨立每强,易擴展始腾。 2、便于控制細節(jié)風險空执。
缺點: 1浪箭、產品必須有共同點,范圍有限制辨绊。 2奶栖、如內部變化復雜,會有很多的建造類门坷。
使用場景: 1宣鄙、需要生成的對象具有復雜的內部結構。 2默蚌、需要生成的對象內部屬性本身相互依賴冻晤。
注意事項:與工廠模式的區(qū)別是:建造者模式更加關注與零件裝配的順序。

實現(xiàn)

我們假設一個快餐店的商業(yè)案例绸吸,其中遣钳,一個典型的套餐可以是一個漢堡(Burger)和一杯冷飲(Cold drink)瞪醋。漢堡(Burger)可以是素食漢堡(Veg Burger)或雞肉漢堡(Chicken Burger),它們是包在紙盒中。冷飲(Cold drink)可以是可口可樂(coke)或百事可樂(pepsi)姻成,它們是裝在瓶子中撑毛。
我們將創(chuàng)建一個表示食物條目(比如漢堡和冷飲)的 Item 接口和實現(xiàn) Item 接口的實體類伪阶,以及一個表示食物包裝的 Packing 接口和實現(xiàn) Packing 接口的實體類扬霜,漢堡是包在紙盒中,冷飲是裝在瓶子中稿存。
然后我們創(chuàng)建一個 Meal 類笨篷,帶有 Item 的 ArrayList 和一個通過結合 Item 來創(chuàng)建不同類型的 Meal 對象的 MealBuilder瞳秽。BuilderPatternDemo,我們的演示類使用 MealBuilder 來創(chuàng)建一個 Meal冕屯。


步驟 1
創(chuàng)建一個表示食物條目和食物包裝的接口寂诱。
Item.java

public interface Item {
   public String name();
   public Packing packing();
   public float price();    
}

Packing.java

public interface Packing {
   public String pack();
}

步驟 2
創(chuàng)建實現(xiàn) Packing 接口的實體類。
Wrapper.java

public class Wrapper implements Packing {
 
   @Override
   public String pack() {
      return "Wrapper";
   }
}

Bottle.java

public class Bottle implements Packing {
 
   @Override
   public String pack() {
      return "Bottle";
   }
}

步驟 3
創(chuàng)建實現(xiàn) Item 接口的抽象類安聘,該類提供了默認的功能痰洒。
Burger.java

public abstract class Burger implements Item {
 
   @Override
   public Packing packing() {
      return new Wrapper();
   }
 
   @Override
   public abstract float price();
}

ColdDrink.java

public abstract class ColdDrink implements Item {
 
    @Override
    public Packing packing() {
       return new Bottle();
    }
 
    @Override
    public abstract float price();
}

步驟 4
創(chuàng)建擴展了 Burger 和 ColdDrink 的實體類。
VegBurger.java

public class VegBurger extends Burger {
 
   @Override
   public float price() {
      return 25.0f;
   }
 
   @Override
   public String name() {
      return "Veg Burger";
   }
}

ChickenBurger.java

public class ChickenBurger extends Burger {
 
   @Override
   public float price() {
      return 50.5f;
   }
 
   @Override
   public String name() {
      return "Chicken Burger";
   }
}

Coke.java

public class Coke extends ColdDrink {
 
   @Override
   public float price() {
      return 30.0f;
   }
 
   @Override
   public String name() {
      return "Coke";
   }
}

Pepsi.java

public class Pepsi extends ColdDrink {
 
   @Override
   public float price() {
      return 35.0f;
   }
 
   @Override
   public String name() {
      return "Pepsi";
   }
}

步驟 5
創(chuàng)建一個 Meal 類浴韭,帶有上面定義的 Item 對象丘喻。
Meal.java

import java.util.ArrayList;
import java.util.List;
 
public class Meal {
   private List<Item> items = new ArrayList<Item>();    
 
   public void addItem(Item item){
      items.add(item);
   }
 
   public float getCost(){
      float cost = 0.0f;
      for (Item item : items) {
         cost += item.price();
      }        
      return cost;
   }
 
   public void showItems(){
      for (Item item : items) {
         System.out.print("Item : "+item.name());
         System.out.print(", Packing : "+item.packing().pack());
         System.out.println(", Price : "+item.price());
      }        
   }    
}

步驟 6
創(chuàng)建一個 MealBuilder 類,實際的 builder 類負責創(chuàng)建 Meal 對象念颈。

MealBuilder.java

public class MealBuilder {
 
   public Meal prepareVegMeal (){
      Meal meal = new Meal();
      meal.addItem(new VegBurger());
      meal.addItem(new Coke());
      return meal;
   }   
 
   public Meal prepareNonVegMeal (){
      Meal meal = new Meal();
      meal.addItem(new ChickenBurger());
      meal.addItem(new Pepsi());
      return meal;
   }
}

步驟 7
BuiderPatternDemo 使用 MealBuider 來演示建造者模式(Builder Pattern)泉粉。

BuilderPatternDemo.java

public class BuilderPatternDemo {
   public static void main(String[] args) {
      MealBuilder mealBuilder = new MealBuilder();
 
      Meal vegMeal = mealBuilder.prepareVegMeal();
      System.out.println("Veg Meal");
      vegMeal.showItems();
      System.out.println("Total Cost: " +vegMeal.getCost());
 
      Meal nonVegMeal = mealBuilder.prepareNonVegMeal();
      System.out.println("\n\nNon-Veg Meal");
      nonVegMeal.showItems();
      System.out.println("Total Cost: " +nonVegMeal.getCost());
   }
}

步驟 8
執(zhí)行程序,輸出結果:

Veg Meal
Item : Veg Burger, Packing : Wrapper, Price : 25.0
Item : Coke, Packing : Bottle, Price : 30.0
Total Cost: 55.0


Non-Veg Meal
Item : Chicken Burger, Packing : Wrapper, Price : 50.5
Item : Pepsi, Packing : Bottle, Price : 35.0
Total Cost: 85.5

6.原型模式

原型模式(Prototype Pattern)是用于創(chuàng)建重復的對象榴芳,同時又能保證性能嗡靡。這種類型的設計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式窟感。
這種模式是實現(xiàn)了一個原型接口讨彼,該接口用于創(chuàng)建當前對象的克隆。當直接創(chuàng)建對象的代價比較大時柿祈,則采用這種模式哈误。例如,一個對象需要在一個高代價的數(shù)據(jù)庫操作之后被創(chuàng)建躏嚎。我們可以緩存該對象蜜自,在下一個請求時返回它的克隆,在需要的時候更新數(shù)據(jù)庫卢佣,以此來減少數(shù)據(jù)庫調用重荠。

介紹

意圖:用原型實例指定創(chuàng)建對象的種類,并且通過拷貝這些原型創(chuàng)建新的對象珠漂。
主要解決:在運行期建立和刪除原型晚缩。
何時使用: 1、當一個系統(tǒng)應該獨立于它的產品創(chuàng)建媳危,構成和表示時。 2冈敛、當要實例化的類是在運行時刻指定時待笑,例如,通過動態(tài)裝載抓谴。 3暮蹂、為了避免創(chuàng)建一個與產品類層次平行的工廠類層次時寞缝。 4、當一個類的實例只能有幾個不同狀態(tài)組合中的一種時仰泻。建立相應數(shù)目的原型并克隆它們可能比每次用合適的狀態(tài)手工實例化該類更方便一些荆陆。
如何解決:利用已有的一個原型對象,快速地生成和原型對象一樣的實例集侯。
關鍵代碼: 1被啼、實現(xiàn)克隆操作,繼承 Cloneable棠枉,重寫 clone() 2浓体、原型模式同樣用于隔離類對象的使用者和具體類型(易變類)之間的耦合關系,它同樣要求這些"易變類"擁有穩(wěn)定的接口辈讶。
應用實例: 1命浴、細胞分裂。 2贱除、JAVA 中的 Object clone() 方法生闲。
優(yōu)點:1、性能提高月幌。 2碍讯、逃避構造函數(shù)的約束。
缺點:1飞醉、配備克隆方法需要對類的功能進行通盤考慮冲茸,這對于全新的類不是很難,但對于已有的類不一定很容易缅帘,特別當一個類引用不支持串行化的間接對象轴术,或者引用含有循環(huán)結構的時候。 2钦无、必須實現(xiàn) Cloneable 接口逗栽。
使用場景:1、資源優(yōu)化場景失暂。 2彼宠、類初始化需要消化非常多的資源,這個資源包括數(shù)據(jù)弟塞、硬件資源等凭峡。 3、性能和安全要求的場景决记。 4摧冀、通過 new 產生一個對象需要非常繁瑣的數(shù)據(jù)準備或訪問權限,則可以使用原型模式。 5索昂、一個對象多個修改者的場景建车。 6、一個對象需要提供給其他對象訪問椒惨,而且各個調用者可能都需要修改其值時缤至,可以考慮使用原型模式拷貝多個對象供調用者使用。 7康谆、在實際項目中领斥,原型模式很少單獨出現(xiàn),一般是和工廠方法模式一起出現(xiàn)秉宿,通過 clone 的方法創(chuàng)建一個對象戒突,然后由工廠方法提供給調用者。原型模式已經與 Java 融為渾然一體描睦,大家可以隨手拿來使用膊存。
注意事項:與通過對一個類進行實例化來構造新對象不同的是,原型模式是通過拷貝一個現(xiàn)有對象生成新對象的忱叭。淺拷貝實現(xiàn) Cloneable隔崎,重寫,深拷貝是通過實現(xiàn) Serializable 讀取二進制流韵丑。

實現(xiàn)

我們將創(chuàng)建一個抽象類 Shape 和擴展了 Shape 類的實體類爵卒。下一步是定義類 ShapeCache,該類把 shape 對象存儲在一個 Hashtable 中撵彻,并在請求的時候返回它們的克隆钓株。
PrototypePatternDemo,我們的演示類使用 ShapeCache 類來獲取 Shape 對象陌僵。


步驟 1
創(chuàng)建一個實現(xiàn)了 Clonable 接口的抽象類轴合。
Shape.java

public abstract class Shape implements Cloneable {
   
   private String id;
   protected String type;
   
   abstract void draw();
   
   public String getType(){
      return type;
   }
   
   public String getId() {
      return id;
   }
   
   public void setId(String id) {
      this.id = id;
   }
   
   public Object clone() {
      Object clone = null;
      try {
         clone = super.clone();
      } catch (CloneNotSupportedException e) {
         e.printStackTrace();
      }
      return clone;
   }
}

步驟 2
創(chuàng)建擴展了上面抽象類的實體類。
Rectangle.java

public class Rectangle extends Shape {
 
   public Rectangle(){
     type = "Rectangle";
   }
 
   @Override
   public void draw() {
      System.out.println("Inside Rectangle::draw() method.");
   }
}

Square.java

public class Square extends Shape {
 
   public Square(){
     type = "Square";
   }
 
   @Override
   public void draw() {
      System.out.println("Inside Square::draw() method.");
   }
}

Circle.java

public class Circle extends Shape {
 
   public Circle(){
     type = "Circle";
   }
 
   @Override
   public void draw() {
      System.out.println("Inside Circle::draw() method.");
   }
}

步驟 3
創(chuàng)建一個類碗短,從數(shù)據(jù)庫獲取實體類受葛,并把它們存儲在一個 Hashtable 中。
ShapeCache.java

import java.util.Hashtable;
 
public class ShapeCache {
    
   private static Hashtable<String, Shape> shapeMap 
      = new Hashtable<String, Shape>();
 
   public static Shape getShape(String shapeId) {
      Shape cachedShape = shapeMap.get(shapeId);
      return (Shape) cachedShape.clone();
   }
 
   // 對每種形狀都運行數(shù)據(jù)庫查詢偎谁,并創(chuàng)建該形狀
   // shapeMap.put(shapeKey, shape);
   // 例如总滩,我們要添加三種形狀
   public static void loadCache() {
      Circle circle = new Circle();
      circle.setId("1");
      shapeMap.put(circle.getId(),circle);
 
      Square square = new Square();
      square.setId("2");
      shapeMap.put(square.getId(),square);
 
      Rectangle rectangle = new Rectangle();
      rectangle.setId("3");
      shapeMap.put(rectangle.getId(),rectangle);
   }
}

步驟 4
PrototypePatternDemo 使用 ShapeCache 類來獲取存儲在 Hashtable 中的形狀的克隆。
PrototypePatternDemo.java

public class PrototypePatternDemo {
   public static void main(String[] args) {
      ShapeCache.loadCache();
 
      Shape clonedShape = (Shape) ShapeCache.getShape("1");
      System.out.println("Shape : " + clonedShape.getType());        
 
      Shape clonedShape2 = (Shape) ShapeCache.getShape("2");
      System.out.println("Shape : " + clonedShape2.getType());        
 
      Shape clonedShape3 = (Shape) ShapeCache.getShape("3");
      System.out.println("Shape : " + clonedShape3.getType());        
   }
}

步驟 5
執(zhí)行程序巡雨,輸出結果:

Shape : Circle
Shape : Square
Shape : Rectangle

參考資料:
菜鳥教程之設計模式
CyC2018/CS-Notes/設計模式

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末闰渔,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子铐望,更是在濱河造成了極大的恐慌澜建,老刑警劉巖向挖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蝌以,死亡現(xiàn)場離奇詭異炕舵,居然都是意外死亡,警方通過查閱死者的電腦和手機跟畅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門咽筋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人徊件,你說我怎么就攤上這事奸攻。” “怎么了虱痕?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵睹耐,是天一觀的道長。 經常有香客問我部翘,道長硝训,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任新思,我火速辦了婚禮窖梁,結果婚禮上,老公的妹妹穿的比我還像新娘夹囚。我一直安慰自己纵刘,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布荸哟。 她就那樣靜靜地躺著假哎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鞍历。 梳的紋絲不亂的頭發(fā)上舵抹,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音堰燎,去河邊找鬼掏父。 笑死,一個胖子當著我的面吹牛秆剪,可吹牛的內容都是我干的赊淑。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼仅讽,長吁一口氣:“原來是場噩夢啊……” “哼陶缺!你這毒婦竟也來了?” 一聲冷哼從身側響起洁灵,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤饱岸,失蹤者是張志新(化名)和其女友劉穎掺出,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體苫费,經...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡汤锨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了百框。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闲礼。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖铐维,靈堂內的尸體忽然破棺而出柬泽,到底是詐尸還是另有隱情,我是刑警寧澤嫁蛇,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布锨并,位于F島的核電站,受9級特大地震影響睬棚,放射性物質發(fā)生泄漏第煮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一闸拿、第九天 我趴在偏房一處隱蔽的房頂上張望空盼。 院中可真熱鬧,春花似錦新荤、人聲如沸揽趾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽篱瞎。三九已至,卻和暖如春痒芝,著一層夾襖步出監(jiān)牢的瞬間俐筋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工严衬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留澄者,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓请琳,卻偏偏與公主長得像粱挡,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子俄精,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容

  • 參考資料:菜鳥教程之設計模式 設計模式概述 設計模式(Design pattern)代表了最佳的實踐询筏,通常被有經驗...
    Steven1997閱讀 1,165評論 1 12
  • 設計模式概述 在學習面向對象七大設計原則時需要注意以下幾點:a) 高內聚、低耦合和單一職能的“沖突”實際上竖慧,這兩者...
    彥幀閱讀 3,734評論 0 14
  • Christopher Alexander說過:“每一個模式描述了一個在我們周圍不斷重復發(fā)生的問題嫌套,以及該問題的解...
    愛情小傻蛋閱讀 429評論 0 2
  • “最終你的故事你的生活都與我無關逆屡,真正的放下不是拉黑了各種方式久久不聯(lián)系,而是還保持著偶爾的問候好似什么都沒有發(fā)生...
    kylin進化論閱讀 107評論 0 0
  • 前段時間的第九屆中國電影金掃帚獎的頒獎典禮上沫勿,王寶強親自認領“最令人失望導演獎”,“希望這是第一次味混,也是最后一次。...
    十四號登機門閱讀 940評論 0 3