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

建造者模式(Builder Pattern)

建造者模式使用多個簡單的對象一步一步構(gòu)建成一個復(fù)雜的對象,這種類型的設(shè)計模式也屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。

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

難度系統(tǒng):中級
提出者:Gang Of Four

意圖

將復(fù)雜對象的構(gòu)造與其表示分離,以便相同的構(gòu)造過程可以創(chuàng)建不同的表示

主要解決:主要解決在軟件系統(tǒng)中,有時候面臨著"一個復(fù)雜對象"的創(chuàng)建工作,其通常由各個部分的子對象用一定的算法構(gòu)成;由于需求的變化,這個復(fù)雜對象的各個部分經(jīng)常面臨著劇烈的變化,但是將它們組合在一起的算法卻相對穩(wěn)定。

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

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

關(guān)鍵代碼:建造者:創(chuàng)建和提供實例,導(dǎo)演:管理建造出來的實例的依賴關(guān)系。

解釋

現(xiàn)實世界的例子

想象一個角色扮演游戲的角色生成器。最簡單的選擇是讓電腦為你創(chuàng)建角色。但是如果你想選擇角色細節(jié),如職業(yè)、性別、發(fā)色等。當你所有選擇一步一步都選定好時,角色生成器也就逐步生成了一個角色,這一過程就是建造者模式創(chuàng)建對象的過程

簡而言之

允許你創(chuàng)建不同風格的對象,同時避免構(gòu)造函數(shù)污染。當一個對象可能有多種風格時很有用?;蛘弋攧?chuàng)建對象涉及很多步驟時

維基百科說

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

說到這里,讓我補充一下伸縮構(gòu)造函數(shù)反模式是什么。在這一點或其他方面,我們可能都見過類似下面這樣的構(gòu)造函數(shù)

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

正如你可以看到的未來那樣,這個構(gòu)造函數(shù)參數(shù)的數(shù)量會很快失控,并且很難理解參數(shù)的排列順序和組合。此外,如果你在未來增加更多選項,這個參數(shù)列表可能會繼續(xù)增長。這稱為伸縮構(gòu)造函數(shù)反模式

程序代碼示例

上面的N參數(shù)構(gòu)造函數(shù)示例,明智的選擇是使用建造者模式。首先,我們有我們想要創(chuàng)造的英雄

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;
  }
}

然后我們設(shè)計建造者

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();

應(yīng)用場景

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

  • 創(chuàng)建復(fù)雜對象的算法應(yīng)該獨立于組成對象的部件以及它們是如何組裝的
  • 構(gòu)建過程必須允許對構(gòu)建的對象進行不同的表示

Java中的現(xiàn)實例子

優(yōu)缺點

優(yōu)點: 1、建造者獨立,易擴展。 2、便于控制細節(jié)風險。

缺點: 1、產(chǎn)品必須有共同點,范圍有限制。 2、如內(nèi)部變化復(fù)雜,會有很多的建造類。

寫在最后

建造者模式又稱之為生成器模式,一般來說有三個角色:建造者、具體的建造者、監(jiān)工角色,為了形象的說明這三個角色的結(jié)構(gòu)和定義我們自己來設(shè)計一個程序?qū)嵗?/p>

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

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

按照上面的假設(shè)需求,我們首先設(shè)計程序類圖如下:


image.png

接下來編寫程序
步驟一:創(chuàng)建文檔接口和編寫工具接口

public interface Document {

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

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

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

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

步驟二:編寫WriteTool接口的實現(xiàn)類

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接口的實現(xiàn)類

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());
        }
    }

}

步驟五:編寫監(jiān)工CopyWriterBuilder

//將一個復(fù)雜對象的構(gòu)建過程與其表示相分離
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輸出結(jié)果如下:

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

下一章節(jié)我將介紹原型模式(Prototype Pattern)這將是最后一個創(chuàng)建型模式

碼字不易,各位看官如果喜歡的話,請給點個喜歡??,關(guān)注下我。我將努力持續(xù)不斷的為大家更新完此系列

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 設(shè)計模式概述 在學習面向?qū)ο笃叽笤O(shè)計原則時需要注意以下幾點:a) 高內(nèi)聚、低耦合和單一職能的“沖突”實際上,這兩者...
    彥幀閱讀 3,898評論 0 14
  • 沒有人買車會只買一個輪胎或者方向盤,大家買的都是一輛包含輪胎、方向盤和發(fā)動機等多個部件的完整汽車。如何將這些部件組...
    justCode_閱讀 2,009評論 1 6
  • 設(shè)計模式匯總 一、基礎(chǔ)知識 1. 設(shè)計模式概述 定義:設(shè)計模式(Design Pattern)是一套被反復(fù)使用、多...
    MinoyJet閱讀 4,096評論 1 15
  • 【學習難度:★★★★☆,使用頻率:★★☆☆☆】直接出處:建造者模式梳理和學習:https://github.com...
    BruceOuyang閱讀 879評論 0 5
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,699評論 19 139

友情鏈接更多精彩內(nèi)容