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

ClericYi發表於2020-02-21

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

前言

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

思維導圖

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

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

單例模式

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

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

5種寫法及其優缺點

(1) 餓漢模式

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

在類載入時就已經完成了初始化。

優點:1. 保障了執行緒同步的問題;2. 獲取物件的效率高。

缺點:1. 降低了類載入時速度;2. 如果一直不使用,會記憶體的浪費。

(2) 懶漢模式

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

缺點:存線上程同步問題 2. 執行緒安全

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

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

懶漢模式相較於餓漢模式,不會存在不使用的問題。雖然不再在載入時消耗資源,但是例項化時同樣會有一定的時間開銷。

(3) 雙重檢查模式/DCL

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

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

存在失效的原因(對使用volatile的解釋)

因為JVM的載入順序是一個無序狀態,他可能進行過指令優化的重排操作,那這種情況就是我們不可控制的,而volatile起著不被忽略的作用,保證了我們的instance不被指令重排。這也就是他的優化方法。

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

public class Singleton {
    private 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;
    private Singleton(){}
}
複製程式碼

這是一種一般不常用的方法,但是能夠保障執行緒同步。

簡單工廠模式

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

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

既然是一個工廠就舉一個工廠的例子。

一般來說的工廠模式都是這樣的: 客戶 --> 工廠 --> 產品

所以產生了三個類

/**
 * 工廠類
 */
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方法
}
複製程式碼

以上就是我的學習成果,如果有什麼我沒有思考到的地方或是文章記憶體在錯誤,歡迎與我分享。


相關文章推薦:

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

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

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

相關文章