聊一聊設計模式(二)-- 建立型設計模式

前言

昨天講了六大原則,今天就進入設計模式的正題了。java

思惟導圖

建立型設計模式,顧名思義就是與對象的建立相關。設計模式

單例模式

定義:保證一個類僅有一個實例,並提供用於一個訪問它的全局訪問點。安全

5種寫法及其優缺點

(1) 餓漢模式ide

public class Singleton {
    private static Singleton instance = new Singleton();
    
    public static Singleton getInstance(){
        return instance;
    }
}
複製代碼

在類加載時就已經完成了初始化。 優勢:1. 保障了線程同步的問題;2. 獲取對象的效率高。 缺點:1. 下降了類加載時速度;2. 若是一直不使用,會內存的浪費。函數

(2) 懶漢模式學習

  1. 線程不安全
public class Singleton {
    private static Singleton instance;
    
    public static Singleton getInstance(){
        if(instance == null) instance = new Singleton();
        return instance;
    }
}
複製代碼

缺點:存在線程同步問題 2. 線程安全ui

public class Singleton {
    private static Singleton instance;
    
    public static synchronized Singleton getInstance(){
        if(instance == null) instance = new Singleton();
        return instance;
    }
}
複製代碼

缺點:每一次都須要同步,存在必定的開銷問題。this

懶漢模式相較於餓漢模式,不會存在不使用的問題。雖然再也不在加載時消耗資源,可是實例化時一樣會有必定的時間開銷。 (3) 雙重檢查模式/DCLspa

public class Singleton {
    private volatile static Singleton instance;

    public static Singleton getInstance(){
        if(instance == null) {
            synchronized (Singleton.class){
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
複製代碼

使用volatile關鍵詞是對正確性的一種保障。 相較於懶漢模式而言,這又是一種升級。由於不在將synchronized套在了函數上,也就不會每次調用都對整個函數同步了,提升了資源的利用率。可是一樣存在失效的狀況。線程

失效案例

(4) 靜態內部類單例模式

public class Singleton {

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }

    private static class SingletonHolder {
        private volatile static Singleton instance = new Singleton();
    }
}
複製代碼

這是最經常使用的方法,也是對DCL的一種升級。 優缺點和前面都差很少,就再也不復述了。 (5) 枚舉單例模式

public enum  Singleton {
    INSTANCE;  
}
複製代碼

這是一種通常不經常使用的方法,可是可以保障線程同步。

簡單工廠模式

定義:又稱靜態工廠模式,是由一個工廠對象決定建立出那一種產品類的實例。

既然是一個工廠就舉一個工廠的例子。 通常來講的工廠模式都是這樣的: 客戶 --> 工廠 --> 產品 因此產生了三個類

/** * 工廠類 */
public class Factory {
    public static Product product(String type){
        Product product = null;
        switch (type){
            case "雞翅":
                product = new ChickenWing();
                break;
            case "漢堡":
                product = new Hamburger();
                break;
        }
        return product;
    }
}

/** * 抽象產品類 */
public abstract class Product {
    public abstract void finish();
}

/** * 具體產品類 */
public class Hamburger extends Product {
    @Override
    public void finish() {
        System.out.println("漢堡製做完成");
    }
}

public class ChickenWing extends Product {
    @Override
    public void finish() {
        System.out.println("雞翅製做完成");
    }
}
複製代碼

三個類已經完成了對應,就跟你在肯德基吃飯同樣,告訴他一個名字,他就開始生產。

優勢:根據參數得到實例,下降了耦合度。 缺點:不利於擴展功能,由於工廠從一開始就知道能加載的有什麼。 使用場景:(1) 工廠負責建立的對象較少;(2)客戶只須要知道想要什麼,不用關心怎麼生成的時候。

工廠方法模式

定義:定義一個用於建立對象的接口,讓子類決定實例化哪一個類。工廠方法將類的實例化放到子類中實現。

工廠模式中的四個角色:

  1. Product:產品抽象類
  2. Factory:工廠抽象類,返回Product對象
  3. ConcreteProduct:具體產品類
  4. ConcreteFactory:具體工廠類 其實就是對簡單工廠方法的一種升級,由於在簡單工廠方法中,只有產品這一層被抽象出來了。而工廠方法是把工廠也抽象出來。 因此如下代碼是對簡單工廠方法的一種改進。
/** * 抽象工廠類 */
public abstract class Factory {
    public abstract <T extends Product> T product(Class<T> clazz);
}

/** * 具體工廠類 */
public class KFC extends Factory {
    @Override
    public <T extends Product> T product(Class<T> clazz) {
        Product product = null;
        try{
            product = (Product) Class.forName(clazz.getName()).newInstance();
        }catch (Exception e){
            e.printStackTrace();
        }
        return (T) product;
    }
}
複製代碼

使用反射機制,也就解決了簡單工廠中分支可能存在增長的問題了。

建造者模式

講一個複雜對象的構建與它的表示分離,使一樣的構建過程能夠建立不一樣的表示。

四個角色:

  1. Director:導演類,將工做按順序完成。
  2. Builder:生成抽象類,生產產品的模版,具體操做由子類完成。
  3. ConcreteBuilder:生成具體類。
  4. Product:產品類。 跟簡單工廠不同,這一次的主角變成了肯德基裏服務的店員們了,他們負責完成漢堡製做的各項流程,而Builder只是說明要幹什麼事情。
/** * 導演類 */
public class Director {
    Builder builder;
    
    Director(Builder builder){
        this.builder = builder;
    }
    
    public Hamburger create(String meat, String vegetable, String bread){
        builder.cookBread(bread);
        builder.cookMeat(meat);
        builder.cookVegetable(vegetable);
        return builder.finish();
    }
}

/** * Builder抽象類 */
public abstract class Builder {
    public abstract void cookMeat(String meat);
    public abstract void cookBread(String bread);
    public abstract void cookVegetable(String vegetable);
    public abstract Hamburger finish();
}

/** * Builder類 */
public class ChickenHamburgerBuilder extends Builder{

    private Hamburger hamburger = new Hamburger();

    @Override
    public void cookMeat(String meat) {
        hamburger.setMeat(meat);
    }

    @Override
    public void cookBread(String bread) {
        hamburger.setBread(bread);
    }

    @Override
    public void cookVegetable(String vegetable) {
        hamburger.setVegetable(vegetable);
    }

    @Override
    public Hamburger finish() {
        return hamburger;
    }
}

/** * 產品類 */
public class Hamburger {
    private String vegetable;
    private String meat;
    private String bread;

    // 如下省略Getter 和 Setter方法
}
複製代碼

以上就是個人學習成果,若是有什麼我沒有思考到的地方或是文章內存在錯誤,歡迎與我分享。


相關文章推薦:

聊一聊設計模式(一)-- 六大原則

聊一聊設計模式(三)-- 結構型設計模式

聊一聊設計模式(四)-- 行爲型設計模式

相關文章
相關標籤/搜索