建造者模式(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)實例子
- java.lang.StringBuilder
- java.nio.ByteBuffer 還有其他類似的buffers 比如 FloatBuffer, IntBuffer 等等.
- java.lang.StringBuffer
- All implementations of java.lang.Appendable
- Apache Camel builders
優(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è)計程序類圖如下:

接下來編寫程序
步驟一:創(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ù)不斷的為大家更新完此系列