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

来源:https://www.cnblogs.com/qiuluoyuweiliang/archive/2023/01/31/17077599.html
-Advertisement-
Play Games

一篇文章帶你瞭解設計模式——創建者模式 在之前的文章中我們已經學習了設計模式的基本原則和基本分類 下麵我們來介紹第一種設計模式,創建型模式的主要關註點是怎樣創建對象,它的主要特點是“將對象的創建與使用分離”。 下麵我們將從下麵四個方面講述五種創建者模式: 單例模式 工廠模式 原型模式 建造者模式 單 ...


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

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

下麵我們來介紹第一種設計模式,創建型模式的主要關註點是怎樣創建對象,它的主要特點是“將對象的創建與使用分離”。

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

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

單例模式

首先我們來介紹單例模式

單例模式介紹

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

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

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

  • 單例類:只能創建一個實例的類
  • 訪問類:使用單例類

單例模式展示

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

  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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、背景 二、概述 三、下線流程與原理 1.讀取待下線節點列表 2.判斷節點下線模式 3.設置超時時間 4.RMNode 處理下線事件 5.監控節點的狀態、下線節點 四、相關的Yarn集群配置 一、背景 接手部門 Hadoop 和 Flink 集群半年了,一直忙著上雲的事兒,很少有時間去琢磨運維的事 ...
  • 前言 SELinux是什麼 安全增強型 Linux(SELinux)是一種採用安全架構的 Linux® 系統,它能夠讓管理員更好地管控哪些人可以訪問系統。它最初是作為 Linux 內核的一系列補丁,由美國國家安全局(NSA)利用 Linux 安全模塊(LSM)開發而成。 SELinux工作原理 SE ...
  • MySQL具有開源免費,運維簡單,性能好等優點,是在汽車之家使用最多的一種資料庫。資料庫作為應用的後端存儲,承擔著數據持久化存儲的功能,是應用可以正常對外提供服務的關鍵組件,資料庫的高可用非常重要。 相對於成熟的商業資料庫軟體,開源的 MySQL高可用需要使用者自己進行設計和研發,本文介紹汽車... ...
  • Flutter 3.7 發佈,本人對其中後臺 isolate 通道比較感興趣,迫不及待翻譯了下Aaron Clarke文章,第一次翻譯,有不足地方歡迎各位大佬們評論區指正,我將持續更新到本文,謝謝。 原文地址:Introducing background isolate channels | by ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 這樣封裝列表 hooks,一天可以開發 20 個頁面 前言 在做移動端的需求時,我們經常會開發一些列表頁,這些列表頁大多數有著相似的功能:分頁獲取列表、上拉載入、下拉刷新··· 在 Vue 出來 compositionAPI 之前,我們想 ...
  • 下麵是實現移動端 H5 拍照功能的幾種方法: 1、使用 <input type="file">:通過 HTML5 規範中的 <input type="file"> 調用系統攝像頭,並選擇拍攝的照片。但這種方式可能會導致頁面刷新。 實現移動端 H5 拍照功能的代碼: 在 HTML 中創建一個 <inp ...
  • state 有狀態state的組件稱作複雜組件,沒有狀態的組件稱為簡單組件 狀態里存儲數據,數據的改變驅動頁面的展示 <script type="text/babel"> // 創建組件 class Weather extends React.Component { // 構造器調用1次 const ...
  • 1、模塊化的發展過程 var moduleObj = { userName: 'zhangsan', fn: function () { console.log('hello world') } } 使用方式 <html> <head> </head> <body> <script src="a.j ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...