深入理解建造者模式 ——組裝複雜的例項

SnailClimb發表於2019-01-19

Java面試通關手冊(Java學習指南,歡迎Star,會一直完善下去,歡迎建議和指導):github.com/Snailclimb/…

歷史文章回顧:

設計模式專欄

深入理解單例模式

深入理解工廠模式

歷史優質文章推薦:

分散式系統的經典基礎理論

可能是最漂亮的Spring事務管理詳解

面試中關於Java虛擬機器(jvm)的問題看這篇就夠了

無論是在現實世界中還是在軟體系統中,都存在一些複雜的物件,它們擁有多個組成部分,如汽車,它包括車輪、方向盤、傳送機等各種部件。而對於大多數使用者而言,無須知道這些部件的裝配細節,也幾乎不會使用單獨某個部件,而是使用一輛完整的汽車,可以通過建造者模式對其進行設計與描述,建造者模式可以將部件和其組裝過程分開,一步一步建立一個複雜的物件。使用者只需要指定複雜物件的型別就可以得到該物件,而無須知道其內部的具體構造細節.

模式動機

一 建造者模式介紹

1.1 定義

建造者模式(Builder Pattern) 又名生成器模式,是一種物件構建模式。它可以將複雜物件的建造過程抽象出來(抽象類別),使這個抽象過程的不同實現方法可以構造出不同表現(屬性)的物件。

建造者模式 是一步一步建立一個複雜的物件,它允許使用者只通過指定複雜物件的型別和內容就可以構建它們,使用者不需要知道內部的具體構建細節。

建造者模式

1.2 為什麼要用建造者模式(優點)?

1) 客戶端不必知道產品內部組成的細節,將產品本身與產品的建立過程解耦,使得相同的建立過程可以建立不同的產品物件。

2) 每一個具體建造者都相對獨立,而與其他的具體建造者無關,因此可以很方便地替換具體建造者或增加新的具體建造者, 使用者使用不同的具體建造者即可得到不同的產品物件 。

3) 可以更加精細地控制產品的建立過程 。將複雜產品的建立步驟分解在不同的方法中,使得建立過程更加清晰,也更方便使用程式來控制建立過程。

4) 增加新的具體建造者無須修改原有類庫的程式碼,指揮者類針對抽象建造者類程式設計,系統擴充套件方便,符合 “開閉原則”

1.3 哪些情況不要用建造者模式(缺點)?

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

2) 產品內部變化很複雜的情況: 如果產品的內部變化複雜,可能會導致需要定義很多具體建造者類來實現這種變化,導致系統變得很龐大。

1.4 抽象工廠模式VS建造者模式

抽象工廠模式實現對產品家族的建立,一個產品家族是這樣的一系列產品:具有不同分類維度的產品組合,採用抽象工廠模式不需要關心構建過程,只關心什麼產品由什麼工廠生產即可。而建造者模式則是要求按照指定的藍圖建造產品,它的主要目的是通過組裝零配件而產生一個新產品。

1.4 模式結構

1.4.1 建造者模式的UML結構圖

建造者模式的UML結構圖

1.4.2 建造者模式主要包含四個角色

Product(產品角色): 一個具體的產品物件。

Builder(抽象建造者): 建立一個Product物件的各個部件指定的抽象介面。

ConcreteBuilder(具體建造者): 實現抽象介面,構建和裝配各個部件。

Director(指揮者): 構建一個使用Builder介面的物件。它主要是用於建立一個複雜的物件。它主要有兩個作用,一是:隔離了客戶與物件的生產過程,二是:負責控制產品物件的生產過程。

二 建造者模式分析

1 ) 一個典型的複雜物件其類程式碼示例如下:

public class Product 
{
	private String partA; //可以是任意型別
	private String partB;
	private String partC;
	//partA的Getter方法和Setter方法省略
	//partB的Getter方法和Setter方法省略
	//partC的Getter方法和Setter方法省略
}

複製程式碼

2 ) 抽象建造者類中定義了產品的建立方法和返回方法,其典型程式碼如下:

public abstract class Builder
{
	protected Product product=new Product();
	
	public abstract void buildPartA();
	public abstract void buildPartB();
	public abstract void buildPartC();
	
	public Product getResult()
	{
		return product;
	}
} 
複製程式碼

3 ) 具體建造者。實現抽象介面,構建和裝配各個部件,例項程式碼如下:

public class ConcreteBuilder extends Builder{
    public void buildPartA(){
    ...
    }
	public void buildPartB(){
	...
	}
	public void buildPartC(){
	...
	}
}
複製程式碼

4)指揮者類的程式碼示例如下:

建造者模式的結構中還引入了一個指揮者類Director,該類的作用主要有兩個:一方面它隔離了客戶與生產過程;另一方面它負責控制產品的生成過程。指揮者針對抽象建造者程式設計,客戶端只需要知道具體建造者的型別,即可通過指揮者類呼叫建造者的相關方法,返回一個完整的產品物件。

public class Director
{
	private Builder builder;
	//1 構造方法的方式注入builder物件
	public Director(Builder builder)
	{
		this.builder=builder;
	}
	//2 set方法注入builder物件
	public void setBuilder(Builder builder)
	{
		this.builder=builer;
	}
	
	public Product construct()
	{
		builder.buildPartA();
		builder.buildPartB();
		builder.buildPartC();
		return builder.getResult();
	}
} 

複製程式碼

5 ) 客戶端類程式碼片段:

在客戶端程式碼中,無須關心產品物件的具體組裝過程,只需確定具體建造者的型別即可,建造者模式將複雜物件的構建與物件的表現分離開來,這樣使得同樣的構建過程可以建立出不同的表現

……
Builder builder = new ConcreteBuilder();
Director director = new Director(builder);
Product product = director.construct();
…… 

複製程式碼

三 例項:KFC套餐

建造者模式可以用於描述KFC如何建立套餐:套餐是一個複雜物件,它一般包含主食(如漢堡、雞肉卷等)和飲料(如果汁、可樂等)等組成部分,不同的套餐有不同的組成部分,而KFC的服務員可以根據顧客的要求,一步一步裝配這些組成部分,構造一份完整的套餐,然後返回給顧客。

例項:KFC套餐

1)Product(產品角色)

一個具體的產品物件。

public class Meal {
	private String food;
	private String drink;

	public String getFood() {
		return food;
	}

	public void setFood(String food) {
		this.food = food;
	}

	public String getDrink() {
		return drink;
	}

	public void setDrink(String drink) {
		this.drink = drink;
	}
}

複製程式碼

2)Builder(抽象建造者)

建立一個Product物件的各個部件指定的抽象介面。

public abstract class MealBuilder {
    Meal meal = new Meal();
    
    public abstract void buildFood();
    
    public abstract void buildDrink();
    
    public Meal getMeal(){
        return meal;
    }
}
複製程式碼

3) ConcreteBuilder(具體建造者)

實現抽象介面,構建和裝配各個部件。

A套餐:

public class MealA extends MealBuilder{

    public void buildDrink() {
        meal.setDrink("可樂");
    }

    public void buildFood() {
        meal.setFood("薯條");
    }

}
複製程式碼

B套餐:

public class MealB extends MealBuilder{

    public void buildDrink() {
        meal.setDrink("檸檬果汁");
    }

    public void buildFood() {
        meal.setFood("雞翅");
    }

}
複製程式碼

4)Director(指揮者)

構建一個使用Builder介面的物件。它主要是用於建立一個複雜的物件,它主要有兩個作用,一是:隔離了客戶與物件的生產過程,二是:負責控制產品物件的生產過程。

public class KFCWaiter {
    private MealBuilder mealBuilder;
    
    public KFCWaiter(MealBuilder mealBuilder) {
    	this.mealBuilder = mealBuilder;
	}
    

    public Meal construct(){
        //準備食物
        mealBuilder.buildFood();
        //準備飲料
        mealBuilder.buildDrink();
        
        //準備完畢,返回一個完整的套餐給客戶
        return mealBuilder.getMeal();
    }
}
複製程式碼

5)測試類(客戶端類)

public class Test {
    public static void main(String[] args) {

        //套餐A
        MealA a = new MealA();
        //準備套餐A的服務員
        KFCWaiter waiter = new KFCWaiter(a);
        //獲得套餐
        Meal mealA = waiter.construct();      
        System.out.print("套餐A的組成部分:");
        System.out.println("食物:"+mealA.getFood()+";   "+"飲品:"+mealA.getDrink());
    }
}

複製程式碼

輸出結果:

套餐A的組成部分:食物:薯條;   飲品:可樂

複製程式碼

四 總結

本文首先介紹了建造者模型包括建造者模型的定義、為什麼要用它、那些情況不適合使用這種模式以及抽象工廠模式和建造者模式的區別的簡單分析。

然後通過建造者模式的四個角色的常見示例程式碼,通過程式碼層面分析了建造者模式。

最後通過一個KFC套餐例項,介紹了建造者模式在例項中的基本使用手段。

參考:

《設計模式之禪》

《圖解設計模式》

我是Snailclimb,一個以架構師為5年之內目標的小小白。 歡迎關注我的微信公眾號:"Java面試通關手冊"(一個有溫度的微信公眾號,期待與你共同進步~~~堅持原創,分享美文,分享各種Java學習資源)

最後,就是使用阿里雲伺服器一段時間後,感覺阿里雲真的很不錯,就申請做了阿里雲大使,然後這是我的優惠券地址.

相關文章