一篇文章帶你瞭解設計模式——建立者模式

2023-01-31 06:00:27

一篇文章帶你瞭解設計模式——建立者模式

在之前的文章中我們已經學習了設計模式的基本原則和基本分類

下面我們來介紹第一種設計模式,建立型模式的主要關注點是怎樣建立物件,它的主要特點是「將物件的建立與使用分離」。

下面我們將從下面四個方面講述五種建立者模式:

  • 單例模式
  • 工廠模式
  • 原型模式
  • 建造者模式

單例模式

首先我們來介紹單例模式

單例模式介紹

單例模式在我的文章中已經是第三次出現了,所以下面我們做一個簡單的介紹:

  • 單例模式屬於建立型模式,它提供了一種建立物件的最佳方式
  • 該模式涉及到一個單一的類,該類負責建立自己的物件,同時確保只有單個物件被建立。
  • 這個類提供了一種存取其唯一的物件的方式,可以直接存取,不需要範例化該類的物件。

單例模式主要有以下角色:

  • 單例類:只能建立一個範例的類
  • 存取類:使用單例類

單例模式展示

單例模式的建立主要分為三種,下面我們來一一介紹:

  1. 餓漢式
/*

餓漢式:類載入就會導致該單範例物件被建立	

*/

/**
 * 餓漢式
 *      靜態變數建立類的物件
 */
public class Singleton {
    // 私有構造方法
    private Singleton() {}

    // 在成員位置建立該類的物件並直接初始化
    private static Singleton instance = new Singleton();

    // 對外提供靜態方法獲取該物件
    public static Singleton getInstance() {
        return instance;
    }
}

/**
 * 餓漢式
 *      在靜態程式碼塊中建立該類物件
 */
public class Singleton {

    //私有構造方法
    private Singleton() {}

    //在成員位置建立該類的物件
    private static Singleton instance;

    // 直接在靜態程式碼塊中進行初始化,一旦類產生就直接建立單例物件,為餓漢式
    static {
        instance = new Singleton();
    }

    //對外提供靜態方法獲取該物件
    public static Singleton getInstance() {
        return instance;
    }
}
  1. 懶漢式
/*

懶漢式:類載入不會導致該單範例物件被建立,而是首次使用該物件時才會建立

*/

/**
 * 懶漢式
 *  執行緒不安全
 */
public class Singleton {
    //私有構造方法
    private Singleton() {}

    //在成員位置建立該類的物件
    private static Singleton instance;

    //對外提供靜態方法獲取該物件
    public static Singleton getInstance() {

        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

/**
 * 懶漢式
 *  執行緒安全
 */
public class Singleton {
    //私有構造方法
    private Singleton() {}

    //在成員位置建立該類的物件
    private static Singleton instance;

    //對外提供靜態方法獲取該物件
    public static synchronized Singleton getInstance() {

        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

/**
 * 懶漢式
 * 雙重檢查方式
 */
public class Singleton {

    //私有構造方法
    private Singleton() {}

    private static volatile Singleton instance;

   //對外提供靜態方法獲取該物件
    public static Singleton getInstance() {
		//第一次判斷,如果instance不為null,不進入搶鎖階段,直接返回實際
        if(instance == null) {
            synchronized (Singleton.class) {
                //搶到鎖之後再次判斷是否為空
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

/**
 * 懶漢式
 * 靜態內部類方式
 */
public class Singleton {

    //私有構造方法
    private Singleton() {}

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    //對外提供靜態方法獲取該物件
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
  1. 惡漢式
/*

惡漢式:採用非類的方法建立單例物件

*/

/**
 * 惡漢式
 * 	列舉方式
 */
public enum Singleton {
    INSTANCE;
}

單例模式應對策略

單例模式通常會出現三種錯誤,其中兩種是可以處理的:

  1. 預防反射破壞單例物件
/*

反射破壞單例物件原理:
	通過反射獲得類本身,通過類的構造方法建立新的類物件以破壞單例物件
	
預防反射破壞單例物件原理:
	修改類的構造方法,使構造方式失效或者使構造方法直接返回單例物件而不是建立新的物件

*/

/*程式碼展示*/

public class Singleton implements Serializable{
    
    // 首先我們需要擁有一個私有的構造方法(為了防止其他物件呼叫構造方法產生新物件)
    private Singleton(){
        
        // 這裡我們需要做一個判斷,如果已存在單例物件,且其他物件呼叫構造方法,直接報錯(為了預防反射獲得類然後新創物件)
        if( INSTANCE != null){
            throw new RuntimeException("單例物件不可重複建立");
        }
        
        System.out.println("private Singleton");
        
    }
    
    private static final Singleton INSTANCE = new Singleton();
    
    public static Singleton getInstance(){
        return INSTANCE;
    }
    
    public static void otherMethod(){
        System.out.println("otherMethod");
    }
    
}
  1. 預防反序列化破壞單例物件
/*

反序列化破壞單例物件原理:
	呼叫readResolve方法使用位元組流輸入輸出獲得新的物件

預防反序列化破壞單例物件原理:
	重寫readResolve方法使其直接返回單例物件

*/

/*程式碼展示*/

public class Singleton implements Serializable {

    //私有構造方法
    private Singleton() {}

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    //對外提供靜態方法獲取該物件
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
    
    /**
     * 下面是為了解決序列化反序列化破解單例模式
     */
    private Object readResolve() {
        return SingletonHolder.INSTANCE;
    }
}

工廠模式

下面我們來介紹工廠模式,主要會介紹到三種工廠模式的使用

工廠模式思考

在正式介紹工廠模式之前,我們首先給出沒有工廠模式會出現的問題:

具體問題:

/*

需求:
	設計一個咖啡店點餐系統。  

具體想法:
	設計一個咖啡類(Coffee),並定義其兩個子類(美式咖啡【AmericanCoffee】和拿鐵咖啡【LatteCoffee】);
	再設計一個咖啡店類(CoffeeStore),咖啡店具有點咖啡的功能。
		
問題展示:
	在java中,萬物皆物件,這些物件都需要建立,如果建立的時候直接new該物件,就會對該物件耦合嚴重,假如我們要更換物件,所有new物件的地方都需要修改一遍,這顯然違背了軟體設計的開閉原則。如果我們使用工廠來生產物件,我們就只和工廠打交道就可以了,徹底和物件解耦,如果要更換物件,直接在工廠裡更換該物件即可,達到了與物件解耦的目的;所以說,工廠模式最大的優點就是:解耦。

*/

簡單工廠模式

簡單工廠模式不是二十三種設計模式成員,他更傾向於是一種思想

簡單工廠模式結構

簡單工廠包含如下角色:

  • 抽象產品 :定義了產品的規範,描述了產品的主要特性和功能。
  • 具體產品 :實現或者繼承抽象產品的子類
  • 具體工廠 :提供了建立產品的方法,呼叫者通過該方法來獲取產品。

簡單工廠模式實戰

我們如果使用簡單工廠模式去修改前面的問題:

具體分析:

/*

工廠(factory)處理建立物件的細節,一旦有了SimpleCoffeeFactory,CoffeeStore類中的orderCoffee()就變成此物件的客戶,後期如果需要Coffee物件直接從工廠中獲取即可。這樣也就解除了和Coffee實現類的耦合,同時又產生了新的耦合,CoffeeStore物件和SimpleCoffeeFactory工廠物件的耦合,工廠物件和商品物件的耦合。

後期如果再加新品種的咖啡,我們勢必要需求修改SimpleCoffeeFactory的程式碼,違反了開閉原則。工廠類的使用者端可能有很多,比如建立美團外賣等,這樣只需要修改工廠類的程式碼,省去其他的修改操作。

*/

/* 工廠類程式碼 */

public class SimpleCoffeeFactory {

    public Coffee createCoffee(String type) {
        Coffee coffee = null;
        if("americano".equals(type)) {
            coffee = new AmericanoCoffee();
        } else if("latte".equals(type)) {
            coffee = new LatteCoffee();
        }
        return coffee;
    }
}

簡單工廠模式分析

我們分別給出簡單工廠模式的優缺點:

  • 優點

封裝了建立物件的過程,可以通過引數直接獲取物件。把物件的建立和業務邏輯層分開,這樣以後就避免了修改客戶程式碼,如果要實現新產品直接修改工廠類,而不需要在原始碼中修改,這樣就降低了客戶程式碼修改的可能性,更加容易擴充套件。

  • 缺點

增加新產品時還是需要修改工廠類的程式碼,違背了「開閉原則」。

簡單工廠模式擴充套件

我們以簡單工廠模式為基準可以創新出靜態工廠模式(注:靜態工廠模式也不是二十三種設計模式成員):

public class SimpleCoffeeFactory {

    // 將方法設定為靜態,主類可以直接使用工廠的方法而省去建立物件的一步
    public static Coffee createCoffee(String type) {
        Coffee coffee = null;
        if("americano".equals(type)) {
            coffee = new AmericanoCoffee();
        } else if("latte".equals(type)) {
            coffee = new LatteCoffee();
        }
        return coffe;
    }
}

工廠方法模式

工廠方法模式屬於二十三種設計模式成員

定義一個用於建立物件的介面,讓子類決定範例化哪個產品類物件;工廠方法使一個產品類的範例化延遲到其工廠的子類。

工廠方法模式結構

工廠方法模式的主要角色:

  • 抽象工廠(Abstract Factory):提供了建立產品的介面,呼叫者通過它存取具體工廠的工廠方法來建立產品。
  • 具體工廠(ConcreteFactory):主要是實現抽象工廠中的抽象方法,完成具體產品的建立。
  • 抽象產品(Product):定義了產品的規範,描述了產品的主要特性和功能。
  • 具體產品(ConcreteProduct):實現了抽象產品角色所定義的介面,由具體工廠來建立,它同具體工廠之間一一對應。

工廠方法模式實戰

我們如果使用工廠方法模式去修改前面的問題:

具體分析:

/*

要增加產品類時也要相應地增加工廠類,不需要修改工廠類的程式碼了,這樣就解決了簡單工廠模式的缺點。

工廠方法模式是簡單工廠模式的進一步抽象。由於使用了多型性,工廠方法模式保持了簡單工廠模式的優點,而且克服了它的缺點。

*/

/* 咖啡店類 */

public class CoffeeStore {

    private CoffeeFactory factory;

    public CoffeeStore(CoffeeFactory factory) {
        this.factory = factory;
    }

    public Coffee orderCoffee(String type) {
        Coffee coffee = factory.createCoffee();
        coffee.addMilk();
        coffee.addsugar();
        return coffee;
    }
}

/* 抽象工廠 */

public interface CoffeeFactory {

    Coffee createCoffee();
}

/* 具體工廠 */

public class LatteCoffeeFactory implements CoffeeFactory {

    public Coffee createCoffee() {
        return new LatteCoffee();
    }
}

public class AmericanCoffeeFactory implements CoffeeFactory {

    public Coffee createCoffee() {
        return new AmericanCoffee();
    }
}

工廠方法模式分析

我們分別給出工廠方法模式的優缺點:

  • 優點

使用者只需要知道具體工廠的名稱就可得到所要的產品,無須知道產品的具體建立過程;

在系統增加新的產品時只需要新增具體產品類和對應的具體工廠類,無須對原工廠進行任何修改,滿足開閉原則;

  • 缺點

每增加一個產品就要增加一個具體產品類和一個對應的具體工廠類,這增加了系統的複雜度。

抽象工廠模式

抽象工廠模式屬於二十三種設計模式成員

抽象工廠模式實際上就是工廠方法模式的升級模式,他將一系列相關的類的建立都產生在同一個工廠中,使其一個工廠類產生多個產品類

抽象工廠模式結構

抽象工廠模式的主要角色如下:

  • 抽象工廠(Abstract Factory):提供了建立產品的介面,它包含多個建立產品的方法,可以建立多個不同等級的產品。
  • 具體工廠(Concrete Factory):主要是實現抽象工廠中的多個抽象方法,完成具體產品的建立。
  • 抽象產品(Product):定義了產品的規範,描述了產品的主要特性和功能,抽象工廠模式有多個抽象產品。
  • 具體產品(ConcreteProduct):實現了抽象產品角色所定義的介面,由具體工廠來建立,它 同具體工廠之間是多對一的關係。

抽象工廠模式實戰

我們的例題發生了一點點的改變,我們所需要的產品類增多後,單單使用工廠方法模式顯得過於繁雜:

具體分析:

/* 

題目更改:
	現咖啡店業務發生改變,不僅要生產咖啡還要生產甜點,如提拉米蘇、抹茶慕斯等,要是按照工廠方法模式,需要定義提拉米蘇類、抹茶慕斯類、提拉米蘇工廠、抹茶慕斯工廠、甜點工廠類,很容易發生類爆炸情況。其中拿鐵咖啡、美式咖啡是一個產品等級,都是咖啡;提拉米蘇、抹茶慕斯也是一個產品等級;拿鐵咖啡和提拉米蘇是同一產品族(也就是都屬於義大利風味),美式咖啡和抹茶慕斯是同一產品族(也就是都屬於美式風味)。所以這個案例可以使用抽象工廠模式實現。

*/

/* 抽象工廠 */

public interface DessertFactory {

    Coffee createCoffee();

    Dessert createDessert();
}

/* 具體工廠 */

//美式甜點工廠
public class AmericanDessertFactory implements DessertFactory {

    public Coffee createCoffee() {
        return new AmericanCoffee();
    }

    public Dessert createDessert() {
        return new MatchaMousse();
    }
}
//義大利風味甜點工廠
public class ItalyDessertFactory implements DessertFactory {

    public Coffee createCoffee() {
        return new LatteCoffee();
    }

    public Dessert createDessert() {
        return new Tiramisu();
    }

抽象工廠模式分析

我們首先給出抽象工廠模式的適用場景:

  • 當需要建立的物件是一系列相互關聯或相互依賴的產品族時,如電器工廠中的電視機、洗衣機、空調等。

  • 系統中有多個產品族,但每次只使用其中的某一族產品。如有人只喜歡穿某一個品牌的衣服和鞋。

  • 系統中提供了產品的類庫,且所有產品的介面相同,使用者端不依賴產品範例的建立細節和內部結構。

  • 如:輸入法換面板,一整套一起換。生成不同作業系統的程式。

我們再分別給出抽象工廠模式的優缺點:

  • 優點

當一個產品族中的多個物件被設計成一起工作時,它能保證使用者端始終只使用同一個產品族中的物件。

  • 缺點

當產品族中需要增加一個新的產品時,所有的工廠類都需要進行修改。

原型模式

下面我們來介紹原型模式

原型模式介紹

首先我們對原型模式做一個簡單的介紹:

  • 用一個已經建立的範例作為原型,通過複製該原型物件來建立一個和原型物件相同的新物件。

原型模式結構

原型模式包含如下角色:

  • 抽象原型類:規定了具體原型物件必須實現的的 clone() 方法。
  • 具體原型類:實現抽象原型類的 clone() 方法,它是可被複制的物件。
  • 存取類:使用具體原型類中的 clone() 方法來複制新的物件。

原型模式關係圖如下:

原型模式實現

原型模式的實現主要分為兩種實現方式:

  • 淺克隆:建立一個新物件,新物件的屬性和原來物件完全相同,對於非基本型別屬性,仍指向原有屬性所指向的物件的記憶體地址。
  • 深克隆:建立一個新物件,屬性中參照的其他物件也會被克隆,不再指向原有物件地址。

其主要實現方法來自於:

  • Java中的Object類中提供了 clone() 方法來實現淺克隆。
  • Cloneable 介面是上面的類圖中的抽象原型類,而實現了Cloneable介面的子實現類就是具體的原型類。

我們給出一個簡單的原型模式實現:

/* 抽象原型類:實際上就是Object,已經實現了clone方法 */

/* 具體原型類 */

public class Realizetype implements Cloneable {

    public Realizetype() {
        System.out.println("具體的原型物件建立完成!");
    }

    @Override
    protected Realizetype clone() throws CloneNotSupportedException {
        System.out.println("具體原型複製成功!");
        return (Realizetype) super.clone();
    }
}

/* 測試存取類 */

public class PrototypeTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Realizetype r1 = new Realizetype();
        Realizetype r2 = r1.clone();

        System.out.println("物件r1和r2是同一個物件?" + (r1 == r2));
    }
}

原型模式案例

我們首先給出一個簡單的案例:

  • 同一學校的「三好學生」獎狀除了獲獎人姓名不同,其他都相同
  • 可以使用原型模式複製多個「三好學生」獎狀出來,然後在修改獎狀上的名字即可。

那麼我們可以直接使用淺克隆來完成上述案例:

//獎狀類
public class Citation implements Cloneable {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return (this.name);
    }

    public void show() {
        System.out.println(name + "同學:在2020學年第一學期中表現優秀,被評為三好學生。特發此狀!");
    }

    @Override
    public Citation clone() throws CloneNotSupportedException {
        return (Citation) super.clone();
    }
}

//測試存取類
public class CitationTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Citation c1 = new Citation();
        c1.setName("張三");

        //複製獎狀
        Citation c2 = c1.clone();
        //將獎狀的名字修改李四
        c2.setName("李四");

        c1.show();
        c2.show();
    }
}

但是當我們修改了內部屬性,將簡單物件改為了參照物件時,兩者的參照物件都會指向原本的地址,就會導致修改一處造成全部修改

所以我們可以採用深克隆來完成這個操作:

//獎狀類
public class Citation implements Cloneable {
    private Student stu;

    public Student getStu() {
        return stu;
    }

    public void setStu(Student stu) {
        this.stu = stu;
    }

    void show() {
        System.out.println(stu.getName() + "同學:在2020學年第一學期中表現優秀,被評為三好學生。特發此狀!");
    }

    @Override
    public Citation clone() throws CloneNotSupportedException {
        return (Citation) super.clone();
    }
}

//學生類
public class Student {
    private String name;
    private String address;

    public Student(String name, String address) {
        this.name = name;
        this.address = address;
    }

    public Student() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

// 測試存取類
public class CitationTest1 {
    public static void main(String[] args) throws Exception {
        Citation c1 = new Citation();
        Student stu = new Student("張三", "西安");
        c1.setStu(stu);

        //建立物件輸出流物件
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Think\\Desktop\\b.txt"));
        //將c1物件寫出到檔案中
        oos.writeObject(c1);
        oos.close();

        //建立物件出入流物件
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Think\\Desktop\\b.txt"));
        //讀取物件
        Citation c2 = (Citation) ois.readObject();
        //獲取c2獎狀所屬學生物件
        Student stu1 = c2.getStu();
        stu1.setName("李四");

        //判斷stu物件和stu1物件是否是同一個物件
        System.out.println("stu和stu1是同一個物件?" + (stu == stu1));

        c1.show();
        c2.show();
    }
}

建造者模式

最後我們來介紹建造者模式,它將一個複雜物件的構建與表示分離,使得同樣的構建過程可以建立不同的表示。

建造者模式介紹

我們首先來介紹一下建造者模式:

  • 分離了部件的構造(由Builder來負責)和裝配(由Director負責)。 從而可以構造出複雜的物件。這個模式適用於:某個物件的構建過程複雜的情況。
  • 由於實現了構建和裝配的解耦。不同的構建器,相同的裝配,也可以做出不同的物件;相同的構建器,不同的裝配順序也可以做出不同的物件。也就是實現了構建演演算法、裝配演演算法的解耦,實現了更好的複用。
  • 建造者模式可以將部件和其組裝過程分開,一步一步建立一個複雜的物件。使用者只需要指定複雜物件的型別就可以得到該物件,而無須知道其內部的具體構造細節。

建造者模式結構

建造者(Builder)模式包含如下角色:

  • 抽象建造者類(Builder):這個介面規定要實現複雜物件的那些部分的建立,並不涉及具體的部件物件的建立。

  • 具體建造者類(ConcreteBuilder):實現 Builder 介面,完成複雜產品的各個部件的具體建立方法。在構造過程完成後,提供產品的範例。

  • 產品類(Product):要建立的複雜物件。

  • 指揮者類(Director):呼叫具體建造者來建立複雜物件的各個部分,在指導者中不涉及具體產品的資訊,只負責保證物件各部分完整建立或按某種順序建立。

建造者模式關係圖如下:

建造者模式實現

我們同樣通過一個簡單的例子來展示建造者模式:

具體分析:

/*

題目介紹:
	生產自行車是一個複雜的過程,它包含了車架,車座等元件的生產。
	而車架又有碳纖維,鋁合金等材質的,車座有橡膠,真皮等材質。
	對於自行車的生產就可以使用建造者模式。

關係圖介紹:
	這裡Director是指揮者;
	Bike是產品,包含車架,車座等元件;
	Builder是抽象建造者,MobikeBuilder和OfoBuilder是具體的建造者;

*/

/* 具體程式碼展示 */

//自行車類
public class Bike {
    private String frame;
    private String seat;

    public String getFrame() {
        return frame;
    }

    public void setFrame(String frame) {
        this.frame = frame;
    }

    public String getSeat() {
        return seat;
    }

    public void setSeat(String seat) {
        this.seat = seat;
    }
}

// 抽象 builder 類
public abstract class Builder {

    protected Bike mBike = new Bike();

    public abstract void buildFrame();
    public abstract void buildSeat();
    public abstract Bike createBike();
}

//摩拜單車Builder類
public class MobikeBuilder extends Builder {

    @Override
    public void buildFrame() {
        mBike.setFrame("鋁合金車架");
    }

    @Override
    public void buildSeat() {
        mBike.setSeat("真皮車座");
    }

    @Override
    public Bike createBike() {
        return mBike;
    }
}

//ofo單車Builder類
public class OfoBuilder extends Builder {

    @Override
    public void buildFrame() {
        mBike.setFrame("碳纖維車架");
    }

    @Override
    public void buildSeat() {
        mBike.setSeat("橡膠車座");
    }

    @Override
    public Bike createBike() {
        return mBike;
    }
}

//指揮者類
public class Director {
    private Builder mBuilder;

    public Director(Builder builder) {
        mBuilder = builder;
    }

    public Bike construct() {
        mBuilder.buildFrame();
        mBuilder.buildSeat();
        return mBuilder.createBike();
    }
}

//測試類
public class Client {
    public static void main(String[] args) {
        showBike(new OfoBuilder());
        showBike(new MobikeBuilder());
    }
    private static void showBike(Builder builder) {
        Director director = new Director(builder);
        Bike bike = director.construct();
        System.out.println(bike.getFrame());
        System.out.println(bike.getSeat());
    }
}

/* 部分程式碼優化:有些情況下需要簡化系統結構,可以把指揮者類和抽象建造者進行結合 */

// 抽象 builder 類
public abstract class Builder {

    protected Bike mBike = new Bike();

    public abstract void buildFrame();
    public abstract void buildSeat();
    public abstract Bike createBike();
    
    public Bike construct() {
        this.buildFrame();
        this.BuildSeat();
        return this.createBike();
    }
}

建造者模式分析

首先我們給出建造者模式的主要適用場景:

  • 建立的物件較複雜,由多個部件構成,各部件面臨著複雜的變化,但構件間的建造順序是穩定的。
  • 建立複雜物件的演演算法獨立於該物件的組成部分以及它們的裝配方式,即產品的構建過程和最終的表示是獨立的。

然後我們給出建造者模式的優點:

  • 建造者模式的封裝性很好。使用建造者模式可以有效的封裝變化,在使用建造者模式的場景中,一般產品類和建造者類是比較穩定的,因此,將主要的業務邏輯封裝在指揮者類中對整體而言可以取得比較好的穩定性。
  • 在建造者模式中,使用者端不必知道產品內部組成的細節,將產品本身與產品的建立過程解耦,使得相同的建立過程可以建立不同的產品物件。
  • 可以更加精細地控制產品的建立過程 。將複雜產品的建立步驟分解在不同的方法中,使得建立過程更加清晰,也更方便使用程式來控制建立過程。
  • 建造者模式很容易進行擴充套件。如果有新的需求,通過實現一個新的建造者類就可以完成,基本上不用修改之前已經測試通過的程式碼,因此也就不會對原有功能引入風險。符合開閉原則。

最後我們給出建造者模式的缺點:

  • 造者模式所建立的產品一般具有較多的共同點,其組成部分相似
  • 如果產品之間的差異性很大,則不適合使用建造者模式,因此其使用範圍受到一定的限制。

建造者模式擴充套件

建造者模式的另一個營業場景也包括:

  • 當一個類構造器需要傳入很多引數時,如果建立這個類的範例,程式碼可讀性會非常差,而且很容易引入錯誤
  • 此時就可以利用建造者模式進行重構。

我們給出一個簡單例子:

/* 原版程式碼 */

public class Phone {
    private String cpu;
    private String screen;
    private String memory;
    private String mainboard;

    public Phone(String cpu, String screen, String memory, String mainboard) {
        this.cpu = cpu;
        this.screen = screen;
        this.memory = memory;
        this.mainboard = mainboard;
    }

    public String getCpu() {
        return cpu;
    }

    public void setCpu(String cpu) {
        this.cpu = cpu;
    }

    public String getScreen() {
        return screen;
    }

    public void setScreen(String screen) {
        this.screen = screen;
    }

    public String getMemory() {
        return memory;
    }

    public void setMemory(String memory) {
        this.memory = memory;
    }

    public String getMainboard() {
        return mainboard;
    }

    public void setMainboard(String mainboard) {
        this.mainboard = mainboard;
    }

    @Override
    public String toString() {
        return "Phone{" +
                "cpu='" + cpu + '\'' +
                ", screen='" + screen + '\'' +
                ", memory='" + memory + '\'' +
                ", mainboard='" + mainboard + '\'' +
                '}';
    }
}

public class Client {
    public static void main(String[] args) {
        //構建Phone物件
        Phone phone = new Phone("intel","三星螢幕","金士頓","華碩");
        System.out.println(phone);
    }
}

/* 建造者模式程式碼 */

public class Phone {

    private String cpu;
    private String screen;
    private String memory;
    private String mainboard;

    private Phone(Builder builder) {
        cpu = builder.cpu;
        screen = builder.screen;
        memory = builder.memory;
        mainboard = builder.mainboard;
    }

    public static final class Builder {
        private String cpu;
        private String screen;
        private String memory;
        private String mainboard;

        public Builder() {}

        public Builder cpu(String val) {
            cpu = val;
            return this;
        }
        public Builder screen(String val) {
            screen = val;
            return this;
        }
        public Builder memory(String val) {
            memory = val;
            return this;
        }
        public Builder mainboard(String val) {
            mainboard = val;
            return this;
        }
        public Phone build() {
            return new Phone(this);}
    }
    @Override
    public String toString() {
        return "Phone{" +
                "cpu='" + cpu + '\'' +
                ", screen='" + screen + '\'' +
                ", memory='" + memory + '\'' +
                ", mainboard='" + mainboard + '\'' +
                '}';
    }
}

public class Client {
    public static void main(String[] args) {
        Phone phone = new Phone.Builder()
                .cpu("intel")
                .mainboard("華碩")
                .memory("金士頓")
                .screen("三星")
                .build();
        System.out.println(phone);
    }
}

結束語

關於建立者模式我們就介紹到這裡,後面我會繼續更新二十三種設計模式,希望能給你帶來幫助~

附錄

該文章屬於學習內容,具體參考B站黑馬程式設計師的Java設計模式詳解

這裡附上視訊連結:22.設計模式-建立型模式-單例設計模式概述_嗶哩嗶哩_bilibili