iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 13
0
Software Development

從生活中認識Design Pattern系列 第 13

[Day13] 生成器模式 | Builder Pattern

本文同步分享於個人blog

  • 定義


把一個複雜物件的建構與樣貌分離,如此相同的建構過程可以產生不同樣貌的物件

先舉一個簡單的例子:組一台電腦!

現在我想要組一台全新的電腦,基本上需要主機板、硬碟以及cpu

public class Computer {
    private String cpu;
    private String mb;
    private String hdd;
    
    public Computer(String cpu, String mb, String hdd) {
        this.cpu = cpu;
        this.mb = mb;
        this.hdd = hdd;
    }
}

Computer myComputer = new Computer("apple", "apple", "apple");

這樣最基本的電腦就組好了。乍看之下,好像很簡單。但如果我要將配備升級呢?還需要顯示卡、鍵盤、滑鼠。

public class Computer {
    private String cpu; 
    private String mb;
    private String hdd; 
    private String vga;
    private String keyboard;
    private String mouse;

    public Computer(String cpu, String mb, String hdd, String vga, String keyboard, String mouse) {
        this.cpu = cpu;
        this.mb = mb;
        this.hdd = hdd;
        this.vga = vga;
        this.keyboard = keyboard;
        this.mouse = mouse;
    }
}

這樣改了之後其實也算符合需求。不過若是有別人想要組電腦,但已經有鍵盤滑鼠了,他不需要再買一樣的東西,那組電腦的方式勢必要再修改一下。這裏我們可以用overload的方式來解決這個問題。

public class Computer {
    private String cpu; 
    private String mb;
    private String hdd; 
    private String vga;
    private String keyboard; // 選配
    private String mouse; // 選配

    public Computer(String cpu, String mb, String hdd, String vga, String keyboard, String mouse) {
        this.cpu = cpu;
        this.mb = mb;
        this.hdd = hdd;
        this.vga = vga;
        this.keyboard = keyboard;
        this.mouse = mouse;
    }
    public Computer(String cpu, String mb, String hdd, String vga, String mouse) {
        this(cpu, mb, hdd, vga, null, mouse)
    }
    public Computer(String cpu, String mb, String hdd, String vga) {
        this(cpu, mb, hdd, vga, null, null)
    }
}

經過辛苦的更新後,終於可以依照第二第三種需求來組一台電腦了。不過可以發現,如果需求一直增加,那constructor勢必就會一直增加,這樣不單因為參數很多使用起來不方便,而且整體的可讀性及維運性都大大降低。

現在我們回到Builder Pattern的定義,把一個複雜物件的建構與樣貌分離,如此相同的建構過程可以產生不同樣貌的物件。對於Computer來說,cpu這些東西就是元件。而Builder Pattern的目的就是要將這些元件做分離,不要全部擠在一起,然後在依照需求一一組裝起來,建立起各種需求的物件。

  • Builder Pattern 成員


在使用Builder Pattern前,必須先來了解一下Builder Pattern內的四個成員

成員 功用
Product 最終要被建立出來的物件類別。
Builder 用來定義建構物件過程中各必要步驟(方法)的介面。
ConcreteBuilder 實作Builder介面,實際用來建構物件的類別。
Director 負責指揮ConcreteBuilder該如何建構物件。

BP1

藉由Builder的介面先定義好組電腦所需要的方法及步驟。ConcreteBuilder實作Builder,並完成組裝的邏輯。最後經由Director呼叫ConcreteBuilder的實作的組裝步驟來將Product建立出來。


  • Builder Pattern 實作


認識完每個成員的能力之後,就來把每個成員套進剛剛的範例試試。

Product | 最終要被建立出來的物件類別。
class Computer {
    private String cpu; 
    private String mb;
    private String hdd; 
    private String vga;
    private String keyboard; // 選配
    private String mouse; // 選配

    public Computer(String cpu, String mb, String hdd, String vga, String keyboard, String mouse) {
        this.cpu = cpu;
        this.mb = mb;
        this.hdd = hdd;
        this.vga = vga;
        this.keyboard = keyboard;
        this.mouse = mouse;
    }
    @Override
    public String toString() { // 等等輸出時方便顯示
        return "Computer : " + "\n" +
                "cpu : " + this.cpu + "\n" +
                "mb : " + this.mb + "\n" +
                "hdd : " + this.hdd + "\n" +
                "vga : " + this.vga +"\n" +
                "keyboard : "+ this.keyboard +"\n" +
                "mouse : "+ this.mouse;
    }
}
Builder | 用來定義建構物件過程中各必要步驟(方法)的介面。
abstract class ComputerBuilder {
    protected Computer computer;

    public abstract ComputerBuilder setCPU(String cpu);
    public abstract ComputerBuilder setMB(String mb);
    public abstract ComputerBuilder setHDD(String hdd);
    public abstract ComputerBuilder setVGA(String vga);
    public abstract ComputerBuilder setKeyboard(String keyboard);
    public abstract ComputerBuilder setMouse(String mouse);
    public abstract Computer build();
 }

將組裝電腦所需要的方法先定義好讓ConcreteBuilder實作。

ConcreteBuilder | 實作Builder介面,實際用來建構物件的類別
class ComputerFactory extends ComputerBuilder{
    private String cpu; 
    private String mb;
    private String hdd; 
    private String vga;
    private String keyboard;
    private String mouse;
    public ComputerBuilder setCPU(String cpu){
        this.cpu = cpu;
        return this;
    }
    public ComputerBuilder setMB(String mb){
        this.mb = mb;
        return this;
    }
    public ComputerBuilder setHDD(String hdd){
        this.hdd = hdd;
        return this;
    }
    public ComputerBuilder setVGA(String vga){
        this.vga = vga;
        return this;
    }
    public ComputerBuilder setKeyboard(String keyboard){
        this.keyboard = keyboard;
        return this;
    }
    public ComputerBuilder setMouse(String mouse){
        this.mouse = mouse;
        return this;
    }
    public Computer build() {
        return new Computer(cpu, mb, hdd, vga, keyboard, mouse);
    }
}

將邏輯實作在Builder所定義好的方法,提供Director呼叫。

Director | 負責指揮ConcreteBuilder該如何建構物件。
class ComputerStore {

    private ComputerBuilder computerBuilder;

    ComputerStore(ComputerBuilder computerBuilder){

        this.computerBuilder = computerBuilder;
    }
    public Computer mixSpec(){
        Computer computer = computerBuilder
                .setCPU("intel")
                .setMB("Apple")
                .setHDD("acer")
                .setVGA("msi")
                .build();
        return computer;
    }
    public Computer appleSpec(){
        Computer computer = computerBuilder
                .setCPU("Apple")
                .setMB("Apple")
                .setHDD("Apple")
                .setVGA("Apple")
                .build();
        return computer;
    }
}

依照需求呼叫ComputerFactory的方法,將Computer建立起來。

Client | 組電腦拉!!!
public class MyComputer {
   public static void main(String[] args) {
        ComputerFactory cf = new ComputerFactory();
        ComputerStore cs = new ComputerStore(cf);
        Computer c = cs.mixSpec();
        System.out.println(c.toString());

   }
}

output

Computer : 
cpu : intel
mb : Apple
hdd : acer
vga : msi
keyboard : null
mouse : null

BP2

這就是Builder Pattern建立一台電腦的方法。可以發現,相較於前面的範例,Builder Pattern的彈性大了許多,整體維護起來也較為方便。但如果Product內的參數量不是太多,可能就無法感受這個Pattern方便的地方!

  • 小結


Builder Pattern的目標
將元件做分離,依照需求一一組裝起來,建立不同需求的物件。
Builder Pattern的成員
Product:最終要被建構物件的類別。
Builder:用來定義建構物件過程中各必要步驟(方法)的介面。
ConcreteBuilder:實作Builder介面,實際用來建構物件的類別。
Director:負責指揮ConcreteBuilder該如何建構物件。
Builder Pattern的優缺點
優點
1. 方便使用者建立複雜的物件(不需要知道實現過程)。
2. 產品的建造和表示分離,實現瞭解耦。
3. 增加新的具體建造者無需修改原有類庫的程式碼,易於拓展,符合“開閉原則“。

缺點
1. 產品必須有共同點,限制了使用範圍。
2. 如內部變化複雜,會有很多的建造類,難以維護。    
Builder Pattern的使用時機
1. 當組裝物件的邏輯變複雜時。
2. 當未來客戶端需要其他客製化內容時。
  • 範例程式碼


範例:實作Builder Pattern

  • References


聊 Java — Builder Pattern
白話 Design Pattern (四) Builder pattern


上一篇
[Day12] 抽象工廠模式 | Abstract Factory Pattern
下一篇
[Day14] 原型模式 | Prototype Pattern
系列文
從生活中認識Design Pattern30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言