天天看點

從零開始學設計模式(四)—建造者模式(Builder Pattern)

建造者模式(Builder Pattern)

建造者模式使用多個簡單的對象一步一步建構成一個複雜的對象,這種類型的設計模式也屬于建立型模式,它提供了一種建立對象的最佳方式。

一個Builder 類會一步一步構造最終的對象。該Builder 類是獨立于其他對象的

難度系統:中級

提出者:Gang Of Four

意圖

将複雜對象的構造與其表示分離,以便相同的構造過程可以建立不同的表示

主要解決:主要解決在軟體系統中,有時候面臨着"一個複雜對象"的建立工作,其通常由各個部分的子對象用一定的算法構成;由于需求的變化,這個複雜對象的各個部分經常面臨着劇烈的變化,但是将它們組合在一起的算法卻相對穩定。

何時使用:一些基本部件不會變,而其組合經常變化的時候。

如何解決:将變與不變分離開。

關鍵代碼:建造者:建立和提供執行個體,導演:管理建造出來的執行個體的依賴關系。

解釋

現實世界的例子

想象一個角色扮演遊戲的角色生成器。最簡單的選擇是讓電腦為你建立角色。但是如果你想選擇角色細節,如職業、性别、發色等。當你所有選擇一步一步都標明好時,角色生成器也就逐漸生成了一個角色,這一過程就是建造者模式建立對象的過程

簡而言之

允許你建立不同風格的對象,同時避免構造函數污染。當一個對象可能有多種風格時很有用。或者當建立對象涉及很多步驟時

維基百科說

The builder pattern is an object creation software design pattern with the intentions of finding a solution to the telescoping constructor anti-pattern(建造者模式是一種對象建立軟體設計模式,旨在找到伸縮構造器反模式的解決方案)

說到這裡,讓我補充一下伸縮構造函數反模式是什麼。在這一點或其他方面,我們可能都見過類似下面這樣的構造函數

public Hero(Profession profession, String name, HairType hairType, HairColor hairColor, Armor armor, Weapon weapon) {
}
           

正如你可以看到的未來那樣,這個構造函數參數的數量會很快失控,并且很難了解參數的排列順序群組合。此外,如果你在未來增加更多選項,這個參數清單可能會繼續增長。這稱為伸縮構造函數反模式

程式代碼示例

上面的N參數構造函數示例,明智的選擇是使用建造者模式。首先,我們有我們想要創造的英雄

public final class Hero {
  private final Profession profession;
  private final String name;
  private final HairType hairType;
  private final HairColor hairColor;
  private final Armor armor;
  private final Weapon weapon;

  private Hero(Builder builder) {
    this.profession = builder.profession;
    this.name = builder.name;
    this.hairColor = builder.hairColor;
    this.hairType = builder.hairType;
    this.weapon = builder.weapon;
    this.armor = builder.armor;
  }
}
           

然後我們設計建造者

public static class Builder {
    private final Profession profession;
    private final String name;
    private HairType hairType;
    private HairColor hairColor;
    private Armor armor;
    private Weapon weapon;

    public Builder(Profession profession, String name) {
      if (profession == null || name == null) {
        throw new IllegalArgumentException("profession and name can not be null");
      }
      this.profession = profession;
      this.name = name;
    }

    public Builder withHairType(HairType hairType) {
      this.hairType = hairType;
      return this;
    }

    public Builder withHairColor(HairColor hairColor) {
      this.hairColor = hairColor;
      return this;
    }

    public Builder withArmor(Armor armor) {
      this.armor = armor;
      return this;
    }

    public Builder withWeapon(Weapon weapon) {
      this.weapon = weapon;
      return this;
    }

    public Hero build() {
      return new Hero(this);
    }
  }
           

最後我們可以這樣來建造Hero:

Hero mage = new Hero.Builder(Profession.MAGE, "Riobard").withHairColor(HairColor.BLACK).withWeapon(Weapon.DAGGER).build();
           

應用場景

當遇到如下的情況你應該考慮使用建造者模式:

  • 建立複雜對象的算法應該獨立于組成對象的部件以及它們是如何組裝的
  • 建構過程必須允許對建構的對象進行不同的表示

Java中的現執行個體子

優缺點

優點: 1、建造者獨立,易擴充。 2、便于控制細節風險。

缺點: 1、産品必須有共同點,範圍有限制。 2、如内部變化複雜,會有很多的建造類。

寫在最後

建造者模式又稱之為生成器模式,一般來說有三個角色:建造者、具體的建造者、監工角色,為了形象的說明這三個角色的結構和定義我們自己來設計一個程式執行個體。

我們假設要制作一份宣傳文案,一份文案可以包含一個或多個文檔,文檔有三種類型:文字文檔、圖表文檔、圖檔文檔。根據不同類型文檔的組合我們有不同類型的文案生成,如文字文案由純文字文檔組成,圖表文案由圖表文檔和圖檔文檔組成,混合文案由文字文檔、圖表文檔、圖檔文檔三者共同組成。

不同類型的文檔由不同的書寫工具書寫,如文字文檔由MicrosoftWord工具編寫,圖表文檔由MicrosoftExcel工具編寫,圖檔文檔由PhotoShop工具編寫。

按照上面的假設需求,我們首先設計程式類圖如下:

從零開始學設計模式(四)—建造者模式(Builder Pattern)

image.png

接下來編寫程式

步驟一:建立文檔接口和編寫工具接口

public interface Document {

    /**
     *
     * @return 文檔名稱
     */
    String name();

    /**
     *
     * @return 文檔類型
     */
    String type();

    /**
     *
     * @return 書寫工具
     */
    WriteTool writeTool();
}
public interface WriteTool {

    /**
     *
     * @return 傳回書寫工具[名稱]+"write"
     */
    String write();
}
           

步驟二:編寫WriteTool接口的實作類

public class MicrosoftWord implements WriteTool{
    @Override
    public String write() {
        return "MicrosoftWord write";
    }
}

public class MicrosoftExcel implements WriteTool {
    @Override
    public String write() {
        return "MicrosoftExcel write";
    }
}
public class PhotoShop implements WriteTool{
    @Override
    public String write() {
        return "PhotoShop write";
    }
}
           

步驟三:編寫Document接口的實作類

public class Chart implements Document {
    @Override
    public String name() {
        return "chart document";
    }

    @Override
    public String type() {
        return "table";
    }

    @Override
    public WriteTool writeTool() {
        return new MicrosoftExcel();
    }
}

public class Image implements Document {
    @Override
    public String name() {
        return "image document";
    }

    @Override
    public String type() {
        return "image";
    }

    @Override
    public WriteTool writeTool() {
        return new PhotoShop();
    }
}

public class Word  implements Document{
    @Override
    public String name() {
        return "word document";
    }

    @Override
    public String type() {
        return "text";
    }

    @Override
    public WriteTool writeTool() {
        return new MicrosoftWord();
    }
}
           

步驟四:編寫建造者CopyWriter類

/**
 * 不同的文案包含一些不同類型的文檔
 * 定義建造對象的方式方法
 */
public class CopyWriter {

    //包含的文檔
    private  List<Document> documents = new ArrayList<>();

    //名字
    private String name;

    //文案類型  文字 圖表 混合
    private String type;


    public CopyWriter(String name,String type){

        this.name = name;
        this.type = type;
    }

    //添加文檔
    public CopyWriter addDocument(Document document) {

        if (null == document){
            throw new IllegalArgumentException("documnet can not be null");
        }
        this.documents.add(document);
        return this;
    }

    public String name(){
        return this.name;
    }

    public String getType(){
        return this.type;
    }
    //展示文案包含的文檔資訊
    public void showDocuments(){

        for (Document doc:documents)
        {
              System.out.print("name:"+doc.name());
              System.out.print(" type:"+doc.type());
              System.out.println(" writeTool:"+doc.writeTool().write());
        }
    }

}
           

步驟五:編寫監工CopyWriterBuilder

//将一個複雜對象的建構過程與其表示相分離
public class CopyWriterBuilder {

    /**
     * 準備文本類型的文案
     * @return
     */
    public CopyWriter prepareTextCopyWriter(){

        CopyWriter copyWriter = new CopyWriter("TextCopyWriter","text");

        //文本類型的文案隻需要準備文字文檔即可
        copyWriter.addDocument(new Word());

        return copyWriter;
    }

    /**
     * 準備圖表類型的文案
     * @return
     */
    public CopyWriter prepareTableCopyWriter(){

        CopyWriter copyWriter = new CopyWriter("TableCopyWriter","table");

        //圖表類型的文案需要準備圖表文檔和圖檔文檔
        copyWriter.addDocument(new Chart()).addDocument(new Image());
        return copyWriter;
    }

    /**
     * 準備混合類型的文案 包含文本和圖表
     * @return
     */
    public CopyWriter prepareMixCopyWriter(){

        CopyWriter copyWriter = new CopyWriter("MixCopyWriter","Mix");

        //圖表類型的文案需要準備圖表文檔、圖檔文檔、文字文檔
        copyWriter.addDocument(new Chart()).addDocument(new Image()).addDocument(new Word());
        return copyWriter;
    }

}
           

步驟六:最後編寫使用者

public class App {

    public static void main(String[] args){

        CopyWriterBuilder builder = new CopyWriterBuilder();

        CopyWriter txtCopyWriter = builder.prepareTextCopyWriter();
        System.out.println(txtCopyWriter.name());
        txtCopyWriter.showDocuments();
        System.out.println("---------------------------------");
        CopyWriter tableCopyWriter = builder.prepareTableCopyWriter();
        System.out.println(tableCopyWriter.name());
        tableCopyWriter.showDocuments();
        System.out.println("---------------------------------");
        CopyWriter mixCopyWriter = builder.prepareMixCopyWriter();
        System.out.println(mixCopyWriter.name());
        mixCopyWriter.showDocuments();

    }

}
           

運作App輸出結果如下:

TextCopyWriter
name:word document type:text writeTool:MicrosoftWord write
---------------------------------
TableCopyWriter
name:chart document type:table writeTool:MicrosoftExcel write
name:image document type:image writeTool:PhotoShop write
---------------------------------
MixCopyWriter
name:chart document type:table writeTool:MicrosoftExcel write
name:image document type:image writeTool:PhotoShop write
name:word document type:text writeTool:MicrosoftWord write
           

下一章節我将介紹原型模式(Prototype Pattern)這将是最後一個建立型模式

碼字不易,各位看官如果喜歡的話,請給點個喜歡️,關注下我。我将努力持續不斷的為大家更新完此系列