建造者模式

建造者

定義:將一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示

特徵:用戶只需指定需要建造的類型就可以得到他們,建造的過程和細節不需要知道

類型:創建型

適用場景

  • 如果一個對象有非常複雜的內部結構(很多屬性)
  • 想把複雜對象的創建和使用分離

優點:封裝性好,創建和使用分離;擴展性好、建造類之間獨立

缺點

  • 產生多餘的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

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章