目錄
本文的結(jié)構(gòu)如下:
- 引言
- 什么是建造者模式
- 模式的結(jié)構(gòu)
- 典型代碼
- 代碼示例
- Builder模式變種
- 建造者模式與抽象工廠模式
- 優(yōu)點(diǎn)和缺點(diǎn)
- 適用環(huán)境
- 模式應(yīng)用
一旭从、引言
玩過(guò)游戲的應(yīng)該清楚,游戲中有很多角色,這些角色往往都有不同的外形片任,不同的能量值,不同的服飾,不同的武器等等,所以要?jiǎng)?chuàng)造一個(gè)游戲角色垛孔,就要?jiǎng)?chuàng)造構(gòu)成這個(gè)游戲角色不同組成;再比如電腦施敢,電腦是由CPU周荐,主板,內(nèi)存僵娃,顯示器等組成的概作,電腦生廠商需要用這些部件組裝成一部完整的電腦。
在軟件開發(fā)中默怨,創(chuàng)建一個(gè)由多個(gè)部件組成的復(fù)雜產(chǎn)品讯榕,往往使用建造者模式。
二匙睹、什么是建造者模式
建造者模式又稱為生成器模式愚屁,它是一種較為復(fù)雜、使用頻率也相對(duì)較低的創(chuàng)建型模式痕檬,它將客戶端與包含多個(gè)組成部分(或部件)的復(fù)雜對(duì)象的創(chuàng)建過(guò)程分離霎槐。
建造者模式關(guān)注如何一步一步創(chuàng)建一個(gè)的復(fù)雜對(duì)象,不同的具體建造者定義了不同的創(chuàng)建過(guò)程梦谜,且具體建造者相互獨(dú)立丘跌,增加新的建造者非常方便袭景,無(wú)須修改已有代碼,系統(tǒng)具有較好的擴(kuò)展性碍岔。
建造者模式定義如下:
建造者模式(Builder Pattern):將一個(gè)復(fù)雜對(duì)象的構(gòu)建與它的表示分離浴讯,使得同樣的構(gòu)建過(guò)程可以創(chuàng)建不同的表示朵夏。建造者模式是一種對(duì)象創(chuàng)建型模式蔼啦。
建造者模式一步一步創(chuàng)建一個(gè)復(fù)雜的對(duì)象,它允許用戶只通過(guò)指定復(fù)雜對(duì)象的類型和內(nèi)容就可以構(gòu)建它們仰猖,用戶不需要知道內(nèi)部的具體構(gòu)建細(xì)節(jié)捏肢。
三、模式的結(jié)構(gòu)
建造者模式UML類圖如下:
在建造者模式結(jié)構(gòu)圖中包含如下幾個(gè)角色:
- Builder(抽象建造者):它為創(chuàng)建一個(gè)產(chǎn)品Product對(duì)象的各個(gè)部件指定抽象接口饥侵,在該接口中一般聲明兩類方法鸵赫,一類方法是buildPartX(),它們用于創(chuàng)建復(fù)雜對(duì)象的各個(gè)部件躏升;另一類方法是getResult()辩棒,它們用于返回復(fù)雜對(duì)象。Builder既可以是抽象類膨疏,也可以是接口一睁。
- ConcreteBuilder(具體建造者):它實(shí)現(xiàn)了Builder接口,實(shí)現(xiàn)各個(gè)部件的具體構(gòu)造和裝配方法佃却,定義并明確它所創(chuàng)建的復(fù)雜對(duì)象者吁,也可以提供一個(gè)方法返回創(chuàng)建好的復(fù)雜產(chǎn)品對(duì)象。
- Product(產(chǎn)品角色):它是被構(gòu)建的復(fù)雜對(duì)象饲帅,包含多個(gè)組成部件复凳,具體建造者創(chuàng)建該產(chǎn)品的內(nèi)部表示并定義它的裝配過(guò)程。
- Director(指揮者):指揮者又稱為導(dǎo)演類灶泵,它負(fù)責(zé)安排復(fù)雜對(duì)象的建造次序育八,指揮者與抽象建造者之間存在關(guān)聯(lián)關(guān)系,可以在其construct()建造方法中調(diào)用建造者對(duì)象的部件構(gòu)造與裝配方法赦邻,完成復(fù)雜對(duì)象的建造单鹿。客戶端一般只需要與指揮者進(jìn)行交互深纲,在客戶端確定具體建造者的類型仲锄,并實(shí)例化具體建造者對(duì)象(也可以通過(guò)配置文件和反射機(jī)制),然后通過(guò)指揮者類的構(gòu)造函數(shù)或者Setter方法將該對(duì)象傳入指揮者類中湃鹊。
復(fù)雜對(duì)象是指那些包含多個(gè)成員屬性的對(duì)象儒喊,這些成員屬性也稱為部件或零件,如汽車包括方向盤币呵、發(fā)動(dòng)機(jī)怀愧、輪胎等部件侨颈,電子郵件包括發(fā)件人、收件人芯义、主題哈垢、內(nèi)容、附件等部件扛拨。
四耘分、典型代碼
復(fù)雜對(duì)象類典型代碼如下:
public class Product {
//定義部件,部件可以是任意類型绑警,包括值類型和引用類型
private String partA;
private String partB;
private String partC;
public void setPartA(String partA) {
this.partA = partA;
}
public void setPartB(String partB) {
this.partB = partB;
}
public void setPartC(String partC) {
this.partC = partC;
}
}
抽象建造者類定義了產(chǎn)品部件的創(chuàng)建方法和復(fù)雜產(chǎn)品返回方法求泰,典型代碼如下:
public abstract class Builder {
protected Product product = new Product();
public abstract void buildPartA();
public abstract void buildPartB();
public abstract void buildPartC();
public Product getResult(){
return product;
}
}
在ConcreteBuilder中實(shí)現(xiàn)了buildPartX()方法,通過(guò)調(diào)用Product的setPartX()方法可以給產(chǎn)品對(duì)象的成員屬性設(shè)值计盒。不同的具體建造者在實(shí)現(xiàn)buildPartX()方法時(shí)將有所區(qū)別渴频,如setPartX()方法的參數(shù)可能不一樣,在有些具體建造者類中某些setPartX()方法無(wú)須實(shí)現(xiàn)(提供一個(gè)空實(shí)現(xiàn))北启。而這些對(duì)于客戶端來(lái)說(shuō)都無(wú)須關(guān)心卜朗,客戶端只需知道具體建造者類型即可。
public class ConcreteBuilder extends Builder {
public void buildPartA() {
product.setPartA("part A");
}
public void buildPartB() {
product.setPartA("part B");
}
public void buildPartC() {
product.setPartA("part C");
}
}
建造者模式的結(jié)構(gòu)中還有一個(gè)指揮者類Director咕村,該類主要有兩個(gè)作用:一是它隔離了客戶與創(chuàng)建過(guò)程场钉;二是它控制產(chǎn)品的創(chuàng)建過(guò)程,包括某個(gè)buildPartX()方法是否被調(diào)用以及多個(gè)buildPartX()方法調(diào)用的先后次序等培廓。指揮者針對(duì)抽象建造者編程惹悄,客戶端只需要知道具體建造者的類型,即可通過(guò)指揮者類調(diào)用建造者的相關(guān)方法肩钠,返回一個(gè)完整的產(chǎn)品對(duì)象泣港。在實(shí)際生活中也存在類似指揮者一樣的角色,如一個(gè)客戶去購(gòu)買電腦价匠,電腦銷售人員相當(dāng)于指揮者当纱,只要客戶確定電腦的類型,電腦銷售人員可以通知電腦組裝人員給客戶組裝一臺(tái)電腦踩窖。指揮者類典型代碼如下:
public class Director {
private Builder builder;
public Director(Builder builder){
this.builder = builder;
}
public void setBuilder(Builder builder){
this.builder = builder;
}
public Product construct(){
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
return builder.getResult();
}
}
客戶端:
public class Client {
public static void main(String[] args) {
Builder builder = new ConcreteBuilder();
Director director = new Director(builder);
Product product = director.construct();
}
}
在客戶端代碼中坡氯,無(wú)須關(guān)心產(chǎn)品對(duì)象的具體組裝過(guò)程,只需指定具體建造者的類型即可洋腮◇锪可以通過(guò)配置文件來(lái)存儲(chǔ)具體建造者類ConcreteBuilder的類名,使得更換新的建造者時(shí)無(wú)須修改源代碼啥供,系統(tǒng)擴(kuò)展更為方便悯恍。
五、代碼示例
一款視頻播放軟件伙狐,為了給用戶使用提供方便涮毫,不同場(chǎng)景下有不同界面瞬欧,如完整界面、精簡(jiǎn)界面等罢防。不同的界面該播放器的組成元素有所差異艘虎,如在完整界面下將顯示菜單、播放列表咒吐、主窗口野建、控制條等,在精簡(jiǎn)界面下只顯示主窗口和控制條渤滞。
復(fù)雜對(duì)象VideoPlayer:
public class VideoPlayer {
private String type;
private String menu;
private String playerList;
private String mainWindows;
private String controlStrip;
public void setType(String type) {
this.type = type;
}
public void setMenu(String menu) {
this.menu = menu;
}
public void setPlayerList(String playerList) {
this.playerList = playerList;
}
public void setMainWindows(String mainWindows) {
this.mainWindows = mainWindows;
}
public void setControlStrip(String controlStrip) {
this.controlStrip = controlStrip;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("這是一個(gè) ").append(type).append(" 視頻播放器\n\n");
sb.append("該播放器擁有:\n");
if (menu != null) sb.append(menu).append("\n");
if (playerList != null) sb.append(playerList).append("\n");
if (mainWindows != null) sb.append(mainWindows).append("\n");
if (controlStrip != null) sb.append(controlStrip).append("\n");
return sb.toString();
}
}
抽象builder:
public abstract class PlayerBuilder {
protected VideoPlayer player = new VideoPlayer();
public abstract void buildType();
public abstract void buildMenu();
public abstract void buildPlayerList();
public abstract void buildMainWindows();
public abstract void buildControlStrip();
public VideoPlayer getPlayer(){
return player;
}
}
具體builder:
public class FullPlayerBuilder extends PlayerBuilder {
public void buildType() {
player.setType("完整界面");
}
public void buildMenu() {
player.setMenu("菜單");
}
public void buildPlayerList() {
player.setPlayerList("播放列表");
}
public void buildMainWindows() {
player.setMainWindows("主界面");
}
public void buildControlStrip() {
player.setControlStrip("控制條");
}
}
public class SimplePlayerBuilder extends PlayerBuilder {
public void buildType() {
player.setType("精簡(jiǎn)界面");
}
public void buildMenu() {
}
public void buildPlayerList() {
}
public void buildMainWindows() {
player.setMainWindows("主界面");
}
public void buildControlStrip() {
player.setControlStrip("控制條");
}
}
指揮者BuilderDirector:
public class PlayerDirector {
private PlayerBuilder builder;
public PlayerDirector(PlayerBuilder builder){
this.builder = builder;
}
public void setBuilder(PlayerBuilder builder){
this.builder = builder;
}
public VideoPlayer construct(){
builder.buildType();
builder.buildMenu();
builder.buildPlayerList();
builder.buildMainWindows();
builder.buildControlStrip();
return builder.getPlayer();
}
}
客戶端測(cè)試:
public class Client {
public static void main(String[] args) {
PlayerBuilder builder = new FullPlayerBuilder();
PlayerDirector director = new PlayerDirector(builder);
VideoPlayer player = director.construct();
System.out.println(player);
}
}
六贬墩、Builder模式變種
6.1榴嗅、省略Director
Director類在建造者模式中扮演十分重要的作用妄呕,它按一定的順序調(diào)用Builder的buildPartX()方法,像客戶端返回一個(gè)完整的產(chǎn)品嗽测。
但是在有些情況下绪励,為了簡(jiǎn)化系統(tǒng),我們可以將Director和抽象建造者Builder進(jìn)行合并唠粥。
如下:
public abstract class PlayerBuilder {
protected VideoPlayer player = new VideoPlayer();
public abstract void buildType();
public abstract void buildMenu();
public abstract void buildPlayerList();
public abstract void buildMainWindows();
public abstract void buildControlStrip();
public VideoPlayer getPlayer(){
buildType();
buildMenu();
buildPlayerList();
buildMainWindows();
buildControlStrip();
return player;
}
}
public class Client {
public static void main(String[] args) {
PlayerBuilder builder = new SimplePlayerBuilder();
VideoPlayer player = builder.getPlayer();
System.out.println(player);
}
}
6.2疏魏、內(nèi)部Builder
內(nèi)部Builder用起來(lái)是鏈?zhǔn)秸{(diào)用的形式,在Effective Java中晤愧,第二條有提到遇到多個(gè)構(gòu)造器參數(shù)時(shí)要考慮用構(gòu)建器大莫,應(yīng)該還是比較熟悉的。
public class VideoPlayer {
private final String type;
private final String menu;
private final String playerList;
private final String mainWindows;
private final String controlStrip;
private VideoPlayer(Builder builder){
type = builder.type;
menu = builder.menu;
playerList = builder.playerList;
mainWindows = builder.mainWindows;
controlStrip = builder.controlStrip;
}
public static class Builder{
private String type;
private String menu;
private String playerList;
private String mainWindows;
private String controlStrip;
public Builder setType(String type) {
this.type = type;
return this;
}
public Builder setMenu(String menu){
this.menu = menu;
return this;
}
public Builder setPlayerList(String playerList){
this.playerList = playerList;
return this;
}
public Builder setMainWindows(String mainWindows){
this.mainWindows = mainWindows;
return this;
}
public Builder setControlStrip(String controlStrip){
this.controlStrip = controlStrip;
return this;
}
public VideoPlayer build(){
return new VideoPlayer(this);
}
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("這是一個(gè) ").append(type).append(" 視頻播放器\n\n");
sb.append("該播放器擁有:\n");
if (menu != null) sb.append(menu).append("\n");
if (playerList != null) sb.append(playerList).append("\n");
if (mainWindows != null) sb.append(mainWindows).append("\n");
if (controlStrip != null) sb.append(controlStrip).append("\n");
return sb.toString();
}
}
客戶端:
public class Client {
public static void main(String[] args) {
VideoPlayer player = new VideoPlayer.Builder()
.setType("簡(jiǎn)單界面")
.setMainWindows("主界面")
.setControlStrip("控制條")
.build();
System.out.println(player);
}
}
七官份、建造者模式與抽象工廠模式
建造者模式完成的事只厘,好像都可以通過(guò)抽象工廠模式完成,那么區(qū)別是什么舅巷?
- 與建造者模式相比羔味,抽象工廠模式返回的是一系列相關(guān)的產(chǎn)品,這些產(chǎn)品位于不同的產(chǎn)品等級(jí)結(jié)構(gòu)钠右,構(gòu)成產(chǎn)品族赋元,而建造者模式返回的是一個(gè)組裝好的完整產(chǎn)品。抽象工廠模式更像一個(gè)汽車零件生產(chǎn)商飒房,生產(chǎn)不同品牌汽車的各種零件搁凸。而建造者模式更像一個(gè)汽車裝配廠,通過(guò)一系列零件的組裝狠毯,最終生產(chǎn)出的是一個(gè)完整的汽車护糖。
- 抽象工廠模式中,客戶端需實(shí)例化工廠類垃你,然后通過(guò)工廠類獲取所需的產(chǎn)品對(duì)象椅文。而建造者模式更側(cè)重于將復(fù)雜的構(gòu)造對(duì)象的方法交給建造者去做喂很,而客戶端只需要通過(guò)指揮者就能創(chuàng)建一個(gè)完整的產(chǎn)品實(shí)例。
八皆刺、優(yōu)點(diǎn)和缺點(diǎn)
8.1少辣、優(yōu)點(diǎn)
建造者模式的主要優(yōu)點(diǎn)如下:
- 在建造者模式中,客戶端不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié)羡蛾,將產(chǎn)品本身與產(chǎn)品的創(chuàng)建過(guò)程解耦漓帅,使得相同的創(chuàng)建過(guò)程可以創(chuàng)建不同的產(chǎn)品對(duì)象。
- 每一個(gè)具體建造者都相對(duì)獨(dú)立痴怨,而與其他的具體建造者無(wú)關(guān)忙干,因此可以很方便地替換具體建造者或增加新的具體建造者,用戶使用不同的具體建造者即可得到不同的產(chǎn)品對(duì)象浪藻。由于指揮者類針對(duì)抽象建造者編程捐迫,增加新的具體建造者無(wú)須修改原有類庫(kù)的代碼,系統(tǒng)擴(kuò)展方便爱葵,符合“開閉原則”施戴。
- 可以更加精細(xì)地控制產(chǎn)品的創(chuàng)建過(guò)程。將復(fù)雜產(chǎn)品的創(chuàng)建步驟分解在不同的方法中萌丈,使得創(chuàng)建過(guò)程更加清晰赞哗,也更方便使用程序來(lái)控制創(chuàng)建過(guò)程。
8.2辆雾、缺點(diǎn)
建造者模式的主要缺點(diǎn)如下:
- 建造者模式所創(chuàng)建的產(chǎn)品一般具有較多的共同點(diǎn)肪笋,其組成部分相似,如果產(chǎn)品之間的差異性很大度迂,例如很多組成部分都不相同藤乙,不適合使用建造者模式,因此其使用范圍受到一定的限制英岭。
- 如果產(chǎn)品的內(nèi)部變化復(fù)雜湾盒,可能會(huì)導(dǎo)致需要定義很多具體建造者類來(lái)實(shí)現(xiàn)這種變化,導(dǎo)致系統(tǒng)變得很龐大诅妹,增加系統(tǒng)的理解難度和運(yùn)行成本罚勾。
九、適用環(huán)境
在以下情況下可以考慮使用建造者模式:
- 需要生成的產(chǎn)品對(duì)象有復(fù)雜的內(nèi)部結(jié)構(gòu)吭狡,這些產(chǎn)品對(duì)象通常包含多個(gè)成員屬性尖殃。
- 需要生成的產(chǎn)品對(duì)象的屬性相互依賴,需要指定其生成順序划煮。
- 對(duì)象的創(chuàng)建過(guò)程獨(dú)立于創(chuàng)建該對(duì)象的類送丰。在建造者模式中通過(guò)引入了指揮者類,將創(chuàng)建過(guò)程封裝在指揮者類中弛秋,而不在建造者類和客戶類中器躏。
- 隔離復(fù)雜對(duì)象的創(chuàng)建和使用俐载,并使得相同的創(chuàng)建過(guò)程可以創(chuàng)建不同的產(chǎn)品。