天天看點

06 建立型模式之建造者模式(Builder Pattern)

文章目錄

    • 1 介紹
      • 1.1 概述
      • 1.2 結構
    • 2 案例實作
    • 3 優缺點和使用場景
    • 4 模式擴充
    • 5 建立者模式對比
      • 5.1 工廠方法模式VS建造者模式
      • 5.2 抽象工廠模式VS建造者模式

1 介紹

1.1 概述

将一個複雜對象的建構與表示分離,使得同樣的建構過程可以建立不同的表示。

  • 分離了部件的構造(由Builder來負責)和裝配(由Director負責)。 進而可以構造出複雜的對象。這個模式适用于:

    某個對象的建構過程複雜的情況

  • 由于實作了建構和裝配的解耦。不同的建構器,相同的裝配,也可以做出不同的對象;相同的建構器,不同的裝配順序也可以做出不同的對象。也就是實作了建構算法、裝配算法的解耦,實作了更好的複用。
  • 建造者模式可以将

    部件和其組裝過程分開

    ,一步一步建立一個複雜的對象。使用者隻需要指定複雜對象的類型就可以得到該對象,而無須知道其内部的具體構造細節。

1.2 結構

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

  • 抽象建造者類(Builder):這個接口規定要實作複雜對象的那些部分的建立,并不涉及具體的部件對象的建立。
  • 具體建造者類(ConcreteBuilder):實作 Builder 接口,完成複雜産品的各個部件的具體建立方法。在構造過程完成後,提供産品的執行個體。
  • 産品類(Product):要建立的複雜對象。
  • 指揮者類(Director):調用具體建造者來建立複雜對象的各個部分,在指導者中不涉及具體産品的資訊,隻負責保證對象各部分完整建立或按某種順序建立。

2 案例實作

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

這裡Bike是産品,包含車架,車座等元件;Builder是抽象建造者,MobikeBuilder和OfoBuilder是具體的建造者;Director是指揮者。類圖如下:

06 建立型模式之建造者模式(Builder Pattern)
  1. 定義具體的産品: 自行車
package study.wyy.design.builder;

import lombok.Data;

/**
 * @author by wyaoyao
 * @Description
 * @Date 2020/12/10 9:21 下午
 * 具體的産品:自行車
 */
@Data
@ToString
public class Bike {

    private String logo;

    /*****
     * 車架,這裡就簡單模拟,用字元串表示
     */
    private String frame;

    /*****
     * 車座,這裡就簡單模拟,用字元串表示
     */
    private String seat;
}
           
  1. 抽象的建造者類
package study.wyy.design.builder;

/**
 * @author by wyaoyao
 * @Description
 * @Date 2020/12/10 9:23 下午
 * 抽象的抽象建造者類
 */
public abstract class builder {
     protected Bike bike = new Bike();

    /**
     *  @author: wyaoyao
     *  @Date: 2020/12/10 9:25 下午
     *  @Description: 建構logo
     */
    public abstract void buildLogo();

    /**
     *  @author: wyaoyao
     *  @Date: 2020/12/10 9:25 下午
     *  @Description: 建構車架
     */
    public abstract void buildFrame();
    /**
     *  @author: wyaoyao
     *  @Date: 2020/12/10 9:25 下午
     *  @Description: 建構車座
     */
    public abstract void buildSeat();

    public Bike createBike(){
        return bike;
    }
}
           
  1. 具體的建造者類:摩拜自行車建造者類,ofo自行車建造者類

    假設摩拜自行車使用鋁合金車架和真皮車座,ofo自行車使用碳纖維車架和橡膠車座

摩拜自行車建造者類

package study.wyy.design.builder;
/**
 * @author by wyaoyao
 * @Description 摩拜自行車建造者類
 * @Date 2020/12/10 9:29 下午
 */
public class MobikeBuilder extends builder{
    @Override
    public void buildLogo() {
        super.bike.setLogo("摩拜自行車");
    }

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

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

ofo自行車建造者類

package study.wyy.design.builder;

/**
 * @author by wyaoyao
 * @Description: ofo自行車建造者類
 * @Date 2020/12/10 9:31 下午
 */
public class OfoBuilder extends builder{
    @Override
    public void buildLogo() {
        super.bike.setLogo("ofo自行車");
    }

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

    @Override
    public void buildSeat() {
        super.bike.setSeat("橡膠車座");
    }
}
           
  1. 指揮者類,需要持有建造者類,根據不同的建造者直接建造不同的産品
package study.wyy.design.builder;

/**
 * @author by wyaoyao
 * @Description
 * @Date 2020/12/10 9:34 下午
 */
public class Director {

    private Builder mBuilder;

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

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

           
  1. 測試
package study.wyy.design.builder;

/**
 * @author by wyaoyao
 * @Description
 * @Date 2020/12/10 9:36 下午
 */
public class Test {
    public static void main(String[] args) {
        // 建構摩拜自行車
        Builder builder = new MobikeBuilder();
        Director director1 = new Director(builder);
        Bike mobike = director1.construct();

        // 建構ofo自行車
        Builder ofoBuilder = new OfoBuilder();
        Director director2 = new Director(ofoBuilder);
        Bike ofo = director2.construct();

        System.out.println("mobike: " + mobike);
        System.out.println("ofo: " + ofo);


    }
}

           

輸出

mobike: Bike(logo=摩拜自行車, frame=鋁合金車架, seat=真皮車座)
ofo: Bike(logo=ofo自行車, frame=碳纖維車架, seat=橡膠車座)
           

上面示例是 Builder模式的正常用法,指揮者類 Director 在建造者模式中具有很重要的作用,它用于指導具體建構者如何建構産品,控制調用先後次序,并向調用者傳回完整的産品類,但是有些情況下需要簡化系統結構,可以把指揮者類和抽象建造者進行結合

package study.wyy.design.builder;

/**
 * @author by wyaoyao
 * @Description
 * @Date 2020/12/10 9:23 下午
 * 抽象的抽象建造者類
 */
public abstract class Builder {

    protected Bike bike = new Bike();

    /**
     *  @author: wyaoyao
     *  @Date: 2020/12/10 9:25 下午
     *  @Description: 建構logo
     */
    public abstract void buildLogo();

    /**
     *  @author: wyaoyao
     *  @Date: 2020/12/10 9:25 下午
     *  @Description: 建構車架
     */
    public abstract void buildFrame();
    /**
     *  @author: wyaoyao
     *  @Date: 2020/12/10 9:25 下午
     *  @Description: 建構車座
     */
    public abstract void buildSeat();

    public Bike createBike(){
        return bike;
    }

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

           

測試

package study.wyy.design.builder;

/**
 * @author by wyaoyao
 * @Description
 * @Date 2020/12/10 9:36 下午
 */
public class Test2 {
    public static void main(String[] args) {
        // 建構摩拜自行車
        Builder builder = new MobikeBuilder();
        Bike mobike = builder.construct();

        // 建構ofo自行車
        Builder ofoBuilder = new OfoBuilder();
        Bike ofo = ofoBuilder.construct();

        System.out.println("mobike: " + mobike);
        System.out.println("ofo: " + ofo);

    }
}
           

輸出

mobike: Bike(logo=摩拜自行車, frame=鋁合金車架, seat=真皮車座)
ofo: Bike(logo=ofo自行車, frame=碳纖維車架, seat=橡膠車座)
           

這樣做确實簡化了系統結構,但同時也加重了抽象建造者類的職責,也不是太符合單一職責原則,如果construct() 過于複雜,建議還是封裝到 Director 中。

3 優缺點和使用場景

優點:

  • 建造者模式的封裝性很好。使用建造者模式可以有效的封裝變化,在使用建造者模式的場景中,一般産品類和建造者類是比較穩定的,是以,将主要的業務邏輯封裝在指揮者類中對整體而言可以取得比較好的穩定性。
  • 在建造者模式中,

    用戶端不必知道産品内部組成的細節

    ,将産品本身與産品的建立過程解耦,

    使得相同的建立過程可以建立不同的産品對象

  • 可以更加精細地控制産品的建立過程 。将複雜産品的建立步驟分解在不同的方法中,使得建立過程更加清晰,也更友善使用程式來控制建立過程。
  • 建造者模式很容易進行擴充。

    如果有新的需求,通過實作一個新的建造者類就可以完成,基本上不用修改之前已經測試通過的代碼,是以也就不會對原有功能引入風險。符合開閉原則

缺點:

建造者模式所建立的産品一般具有較多的共同點,其組成部分相似,

如果産品之間的差異性很大,則不适合使用建造者模式

,是以其使用範圍受到一定的限制。

使用場景

建造者(Builder)模式建立的是複雜對象,其産品的各個部分經常面臨着劇烈的變化,但将它們組合在一起的算法卻相對穩定,是以它通常在以下場合使用。

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

4 模式擴充

建造者模式除了上面的用途外,在開發中還有一個常用的使用方式,就是當一個類構造器需要傳入很多參數時,如果建立這個類的執行個體,代碼可讀性會非常差,而且很容易引入錯誤,此時就可以利用建造者模式進行重構。

重構前代碼如下:

package study.wyy.design.builder.demo2;

import lombok.Data;
import lombok.ToString;

/**
 * @author by wyaoyao
 * @Description
 * @Date 2020/12/10 9:48 下午
 */
@Data
@ToString
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;
    }
}
           

建構一個phone對象

package study.wyy.design.builder.demo2;

/**
 * @author by wyaoyao
 * @Description
 * @Date 2020/12/10 9:49 下午
 */
public class Test {

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

改造

package study.wyy.design.builder.demo2;

import lombok.Data;
import lombok.ToString;

/**
 * @author by wyaoyao
 * @Description
 * @Date 2020/12/10 9:48 下午
 */
@Data
@ToString
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 static Builder builder(){
        return new Builder();
    }

    /**
     *  @author: wyaoyao
     *  @Date: 2020/12/10 9:54 下午
     *  @Description: private:
     */
    public Phone(Builder builder) {
        this.cpu = builder.cpu;
        this.screen = builder.screen;
        this.memory = builder.memory;
        this.mainboard = builder.mainboard;
    }

    /****
     * phone構造者:使用final修飾,不允許被繼承修改
     */
    public static final class Builder {
        private String cpu;
        private String screen;
        private String memory;
        private String mainboard;

        public Builder() {}

        public Builder cpu(String cpu){
            this.cpu = cpu;
            return this;
        }

        public Builder screen(String screen){
            this.screen = screen;
            return this;
        }

        public Builder memory(String memory){
            this.memory = memory;
            return this;
        }

        public Builder mainboard(String mainboard){
            this.mainboard = mainboard;
            return this;
        }

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

重構後的代碼在使用起來更友善,某種程度上也可以提高開發效率。從軟體設計上,對程式員的要求比較高。

5 建立者模式對比

5.1 工廠方法模式VS建造者模式

工廠方法模式注重的是整體對象的建立方式

;而

建造者模式注重的是部件建構的過程

,意在通過一步一步地精确構造建立出一個複雜的對象。

我們舉個簡單例子來說明兩者的差異,如要制造一個超人,如果使用工廠方法模式,直接産生出來的就是一個力大無窮、能夠飛翔、内褲外穿的超人;而如果使用建造者模式,則需要組裝手、頭、腳、軀幹等部分,然後再把内褲外穿,于是一個超人就誕生了。

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

抽象工廠模式實作對産品家族的建立,一個産品家族是這樣的一系列産品:具有不同分類次元的産品組合,采用抽象工廠模式則是不需要關心建構過程,隻關心什麼産品由什麼工廠生産即可。

建造者模式則是要求按照指定的藍圖建造産品,它的主要目的是通過組裝零配件而産生一個新産品。

如果将抽象工廠模式看成汽車配件生産工廠,生産一個産品族的産品,那麼建造者模式就是一個汽車組裝工廠,通過對部件的組裝可以傳回一輛完整的汽車。