建造者
定義:將一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示
特徵:用戶只需指定需要建造的類型就可以得到他們,建造的過程和細節不需要知道
類型:創建型
適用場景:
- 如果一個對象有非常複雜的內部結構(很多屬性)
- 想把複雜對象的創建和使用分離
優點:封裝性好,創建和使用分離;擴展性好、建造類之間獨立
缺點:
產生多餘的Builder對象
產品內部發生改變,建造者都要修改,成本較大
UML類圖:
抽象建造者(Builder)角色:給 出一個抽象接口,以規範產品對象的各個組成成分的建造。一般而言,此接口獨立於應用程序的商業邏輯。模式中直接創建產品對象的是具體建造者 (ConcreteBuilder)角色。具體建造者類必須實現這個接口所要求的兩種方法:一種是建造方法(buildPart1和 buildPart2),另一種是返還結構方法(retrieveResult)。一般來說,產品所包含的零件數目與建造方法的數目相符。換言之,有多少 零件,就有多少相應的建造方法。
具體建造者(ConcreteBuilder)角色:擔任這個角色的是與應用程序緊密相關的一些類,它們在應用程序調用下創建產品的實例。這個角色要完成的任務包括:1.實現抽象建造者Builder所聲明的接口,給出一步一步地完成創建產品實例的操作。2.在建造過程完成後,提供產品的實例。
指導者(Director)角色:擔任這個角色的類調用具體建造者角色以創建產品對象。應當指出的是,指導者角色並沒有產品類的具體知識,真正擁有產品類的具體知識的是具體建造者角色。
產品(Product)角色:產品便是建造中的複雜對象。一般來說,一個系統中會有多於一個的產品類,而且這些產品類並不一定有共同的接口,而完全可以是不相關聯的
指導者角色是與客戶端打交道的角色。指導者將客戶端創建產品的請求劃分爲對各個零件的建造請求,再將這些請求委派給具體建造者角色。具體建造者角色是做具體建造工作的,但是卻不爲客戶端所知。
一般來說,每有一個產品類,就有一個相應的具體建造者類。這些產品應當有一樣數目的零件,而每有一個零件就相應地在所有的建造者角色裏有一個建造方法
類圖代碼:
- 產品類Product
public class Product {
/**
* 定義一些關於產品的操作
*/
private String part1;
private String part2;
public String getPart1() {
return part1;
}
public void setPart1(String part1) {
this.part1 = part1;
}
public String getPart2() {
return part2;
}
public void setPart2(String part2) {
this.part2 = part2;
}
}
- 抽象建造者類Builder
public interface Builder {
public void buildPart1();
public void buildPart2();
public Product retrieveResult();
}
- 具體建造者類ConcreteBuilder
public class ConcreteBuilder implements Builder {
private Product product = new Product();
/**
* 產品零件建造方法1
*/
@Override
public void buildPart1() {
//構建產品的第一個零件
product.setPart1("編號:9527");
}
/**
* 產品零件建造方法2
*/
@Override
public void buildPart2() {
//構建產品的第二個零件
product.setPart2("名稱:XXX");
}
/**
* 產品返還方法
*/
@Override
public Product retrieveResult() {
return product;
}
}
- 指導者類Director
public class Director {
/**
* 持有當前需要使用的建造器對象
*/
private Builder builder;
/**
* 構造方法,傳入建造器對象
* @param builder 建造器對象
*/
public Director(Builder builder){
this.builder = builder;
}
/**
* 產品構造方法,負責調用各個零件建造方法
*/
public void construct(){
builder.buildPart1();
builder.buildPart2();
}
}
- 客戶端類Client
public class Client {
public static void main(String[]args){
Builder builder = new ConcreteBuilder();
Director director = new Director(builder);
director.construct();
Product product = builder.retrieveResult();
System.out.println(product.getPart1());
System.out.println(product.getPart2());
}
}
客戶端負責創建指導者和具體建造者對象。然後,客戶端把具體建造者對象交給指導者,指導者操作具體建造者,開始創建產品。當產品完成後,建造者把產品返還給客戶端。
把創建具體建造者對象的任務交給客戶端而不是指導者對象,是爲了將指導者對象與具體建造者對象的耦合變成動態的,從而使指導者對象可以操縱數個具體建造者對象中的任何一個。
從上可以看到,這是對多種產品的製造採用了建造者模式,進行了定義了抽象建造者接口,如果產品只有一種,那麼完全可以簡化整個模式,下面的具體示例中會給出代碼和UML圖
示例
建造者模式可以用於描述KFC如何創建套餐:套餐是一個複雜對象,它一般包含主食(如漢堡、雞肉卷等)和飲料(如果汁、可樂等)等組成部分,不同的套餐有不同的組成部分,而KFC的服務員可以根據顧客的要求,一步一步裝配這些組成部分,構造一份完整的套餐,然後返回給顧客。
- 具體的產品對象
/**
* 具體的產品對象
*/
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;
}
}
- 抽象建造者,創建一個Product對象的各個部件指定的抽象接口
/**
* 抽象建造者對象
*/
public abstract class MealBuilder {
Meal meal = new Meal();
public abstract void buildFood();
public abstract void buildDrink();
public Meal getMeal(){
return meal;
}
}
- 具體建造者,構件和裝配各個部件
/**
* 套餐A的建造者
*/
public class MealBuilderA extends MealBuilder {
public void buildDrink() {
meal.setDrink("可樂");
}
public void buildFood() {
meal.setFood("薯條");
}
}
/**
* 套餐B的建造者
*/
public class MealBuilderB extends MealBuilder {
public void buildDrink() {
meal.setDrink("檸檬果汁");
}
public void buildFood() {
meal.setFood("雞翅");
}
}
- 指導者,構建一個使用Builder接口的對象。它主要是用於創建一個複雜的對象,它主要有兩個作用,一是:隔離了客戶與對象的生產過程,二是:負責控制產品對象的生產過程。
/**
* Director(指導者)
*/
public class KFCWaiter {
private MealBuilder mealBuilder;
public KFCWaiter(MealBuilder mealBuilder) {
this.mealBuilder = mealBuilder;
}
public Meal construct(){
//準備食物
mealBuilder.buildFood();
//準備飲料
mealBuilder.buildDrink();
//準備完畢,返回一個完整的套餐給客戶
return mealBuilder.getMeal();
}
}
- 客戶端
/**
* 建造者模式
*/
public class Client {
public static void main(String[] args) {
//套餐A
MealBuilder mealBuilderA = new MealBuilderA(); //準備套餐A的服務員
KFCWaiter waiter = new KFCWaiter(mealBuilderA);
//獲得套餐
Meal mealA = waiter.construct();
System.out.print("套餐A的組成部分:");
System.out.println("食物:"+mealA.getFood()+"; "+"飲品:"+mealA.getDrink());
//套餐B
MealBuilder mealBuilderB = new MealBuilderB(); //準備套餐B的服務員
waiter = new KFCWaiter(mealBuilderB);
//獲得套餐
Meal mealB = waiter.construct();
System.out.print("套餐B的組成部分:");
System.out.println("食物:"+mealB.getFood()+"; "+"飲品:"+mealB.getDrink());
}
}
套餐A的組成部分:食物:薯條; 飲品:可樂
套餐B的組成部分:食物:雞翅; 飲品:檸檬果汁
上面說到,如果我們的產品只有一種,比如,KFC餐廳的只有薯條和可樂這樣一種套餐,那麼我們完全可以簡化建造者模式,由於產品種類固定一種,那麼就無需提供抽象建造者接口,直接提供一個具體的建造者就行,其次,對於創建一個複雜的對象,可能會有很多種不同的選擇和步驟,乾脆去掉“導演者”,把導演者的功能和Client的功能合併起來,也就是說,Client這個時候就相當於指導者,它來指導建造者類去構建需要的複雜對象
- 將產品和建造者合併在一起
public class Meal {
private String food;
private String drink;
//私有化構造,不允許客戶端直接new
private Meal(MealBuilder mealBuilder){
this.food = mealBuilder.food;
this.drink = mealBuilder.drink;
}
public static class MealBuilder{
private String food; //這裏可以設置一些默認值
private String drink; //這裏可以設置一些默認值
public MealBuilder buildFood(){
this.food = "薯條";
return this;
}
public MealBuilder buildDrink(){
this.drink = "可樂";
return this;
}
public Meal getMeal(){
return new Meal(this);
}
}
@Override
public String toString() {
return "Meal{" +
"food='" + food + '\'' +
", drink='" + drink + '\'' +
'}';
}
}
- 客戶端不依賴指導者,直接通過產品中的建造器創建產品實例
/**
* 簡化版的建造者模式
*/
public class Client {
public static void main(String[] args) {
Meal meal = new Meal.MealBuilder().buildFood().buildDrink().getMeal();
System.out.println(meal);
}
}
Meal{food=’薯條’, drink=’可樂’}
相關設計模式:
- 建造者模式注重於方法的調用順序,而工廠模式注重於創建產品,建造者模式創建一些複雜的產品,由複雜的構建組成
- 工廠模式主要是關心的是什麼產品由什麼工廠生產,而建造者模式則是要求按照指定的藍圖建造產品,它的主要目的是通過組裝零配件而產生一個新產品。
使用典範
- org.apache.http.impl.client.HttpClients
- java.lang.StringBuilder
- com.google.common.cache.CacheBuilder
- com.google.common.collect.ImmutableSet