此系列文章為清華大學(xué)出版社出版劉偉編著《Java設(shè)計模式》的學(xué)習(xí)筆記爬虱。
1 概述
1.1 建造者模式概念
將一個復(fù)雜對象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示腾它。建造者模式是一種對象創(chuàng)建模式跑筝,它將客戶端與包含多個部件的復(fù)雜對象的創(chuàng)建過程分離,客戶端無需知道復(fù)雜對象的內(nèi)部組成部分與裝配方式瞒滴,只需要知道所需建造者的類型即可曲梗。建造者模式關(guān)注如何一步一步地創(chuàng)建一個復(fù)雜的對象,不同的建造者定義了不同的創(chuàng)建過程妓忍。
2 結(jié)構(gòu)與實現(xiàn)
2.1 建造者模式結(jié)構(gòu)
建造者模式包含4個角色:
- AbstractBuilder(抽象建造者):它為創(chuàng)建一個產(chǎn)品對象的各個部件指定抽象接口虏两,在該接口中一般聲明兩類方法,一類方法是buildPartX()世剖,它們用于創(chuàng)建復(fù)雜對象的各個部件定罢;另一類方法是getResult(),它們用于返回復(fù)雜對象旁瘫。AbstractBuilder既可以是抽象類祖凫,也可以是接口。
- ConcreteBuilder(具體建造者):它實現(xiàn)了AbstractBuilder中的抽象方法酬凳,實現(xiàn)各個部件的具體構(gòu)造和裝配方法惠况。定義并明確所創(chuàng)建的復(fù)雜對象,還可以提供一個方法返回創(chuàng)建好的復(fù)雜產(chǎn)品對象宁仔。
- Product(產(chǎn)品):它是被構(gòu)建的復(fù)雜對象售滤,包含多個組成部件,具體建造者創(chuàng)建該產(chǎn)品的內(nèi)部表示并定義它的裝配過程台诗。
- Director(指揮者):指揮者又稱導(dǎo)演類完箩,它負(fù)責(zé)安排復(fù)雜對象的建造次序,指揮者與抽象建造者之間存在關(guān)聯(lián)關(guān)系拉队,可以在其construct()建造方法中調(diào)用建造者對象的部件構(gòu)造與裝配方法弊知,完成復(fù)雜對象的建造×豢欤客戶端一般只需要與指揮者進行交互秩彤,在客戶端確定建造者的具體類型,并實例化具體的建造者對象(實例中通過配置文件和反射機制實現(xiàn))事哭,然后通過指揮者的構(gòu)造函數(shù)或者Setter方法將對象傳入指揮者類中漫雷。
2.2 建造者模式舉例
一、背景介紹
某游戲軟件公司決定開發(fā)一款基于角色扮演的多人在線網(wǎng)絡(luò)游戲鳍咱,玩家可以在游戲中扮演虛擬世界中的一個特定角色降盹,角色根據(jù)不同的游戲情節(jié)和統(tǒng)計數(shù)據(jù)(例如力量、魔法谤辜、技能等)具有不同的能力蓄坏,角色也會隨著不斷地升級而擁有更加強大的能力价捧。
作為該游戲的一個重要組成部分,需要對游戲角色進行設(shè)計涡戳,而且隨著該游戲的升級將不斷地增加新的角色结蟋。通過分析發(fā)現(xiàn),角色游戲是一個復(fù)雜對象渔彰,它包含性別嵌屎、臉型等多個組成部分,不同類型地游戲角色恍涂,其性別编整、臉型、服裝乳丰、發(fā)型等外部特性都有所差異,例如“天使”擁有美麗的面容和披肩長發(fā)内贮,并身穿一襲長裙产园;而“惡魔”極其丑陋,留著光頭并身穿一件刺眼的黑衣夜郁。
無論是何種造型的游戲角色什燕,它的創(chuàng)建步驟都大同小異,都需要逐步創(chuàng)建其組成部分竞端,再將各組成部分裝配成一個完整的游戲角色屎即。
請使用建造者模式來實現(xiàn)游戲角色的創(chuàng)建。
二事富、項目結(jié)構(gòu)
三技俐、抽象建造者
public abstract class ActorBuilder {
protected Actor actor = new Actor();
public abstract void buildType();
public abstract void buildSex();
public abstract void buildFace();
public abstract void buildCostume();
public abstract void buildHairstyle();
//todo:工廠方法,返回一個完整的游戲角色對象
public Actor createActor(){
return actor;
}
}
四统台、具體建造者
建造一個“天使”角色
public class AngelBuilder extends ActorBuilder {
@Override
public void buildType() {
actor.setType("天使");
}
@Override
public void buildSex() {
actor.setSex("女");
}
@Override
public void buildFace() {
actor.setFace("漂亮");
}
@Override
public void buildCostume() {
actor.setCostume("白裙");
}
@Override
public void buildHairstyle() {
actor.setHairstyle("長發(fā)");
}
}
建造一個“惡魔”角色
public class DevilBuilder extends ActorBuilder {
@Override
public void buildType() {
actor.setType("惡魔");
}
@Override
public void buildSex() {
actor.setSex("魔");
}
@Override
public void buildFace() {
actor.setFace("丑陋");
}
@Override
public void buildCostume() {
actor.setCostume("黑衣");
}
@Override
public void buildHairstyle() {
actor.setHairstyle("光頭");
}
}
建造一個“英雄”角色
public class HeroBuilder extends ActorBuilder {
@Override
public void buildType() {
actor.setType("英雄");
}
@Override
public void buildSex() {
actor.setSex("男");
}
@Override
public void buildFace() {
actor.setFace("英俊");
}
@Override
public void buildCostume() {
actor.setCostume("披風(fēng)");
}
@Override
public void buildHairstyle() {
actor.setHairstyle("飄逸");
}
}
五雕擂、產(chǎn)品
public class Actor {
private String type;//角色類型
private String sex;//角色性別
private String face;//角色臉型
private String costume;//角色服裝
private String hairstyle;//角色發(fā)行
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getFace() {
return face;
}
public void setFace(String face) {
this.face = face;
}
public String getCostume() {
return costume;
}
public void setCostume(String costume) {
this.costume = costume;
}
public String getHairstyle() {
return hairstyle;
}
public void setHairstyle(String hairstyle) {
this.hairstyle = hairstyle;
}
}
六、指揮者
public class ActorController {
public Actor construct(ActorBuilder ab){
Actor actor;
ab.buildType();
ab.buildSex();
ab.buildFace();
ab.buildCostume();
ab.buildHairstyle();
actor = ab.createActor();
return actor;
}
}
七贱勃、XML確定皮膚類型
編寫config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<config>
<!--<className>CreationalPattern.BuilderPattern.ConcreteBuilder.AngelBuilder</className>-->
<className>CreationalPattern.BuilderPattern.ConcreteBuilder.HeroBuilder</className>
<!--<className>CreationalPattern.BuilderPattern.ConcreteBuilder.DevilBuilder</className>-->
</config>
八井赌、編寫XMLUtil
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
public class XMLUtil {
public static Object getBean(){
try {
//todo:創(chuàng)建 DOM 文檔對象
DocumentBuilderFactory documentBuilderFactory= DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document doc;
doc = documentBuilder.parse("src\\CreationalPattern\\BuilderPattern\\config.xml");
//todo:獲取包含類名的文本節(jié)點
NodeList nodeList = doc.getElementsByTagName("className");
Node classNode = nodeList.item(0).getFirstChild();
String cName = classNode.getNodeValue();
//todo:通過類名生成實例對象并將其返回
Class clz = Class.forName(cName);
Object obj = clz.newInstance();
return obj;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
九、測試Client
public class Client {
public static void main(String[] args) {
//todo:讀取XML文件贵扰,反射創(chuàng)建ActorBuilder對象
ActorBuilder actorBuilder;
actorBuilder = (ActorBuilder)XMLUtil.getBean();
//todo:傳入ActorBuilder對象仇穗,利用ActorController創(chuàng)建具體Actor對象
ActorController actorController = new ActorController();
Actor actor;
actor = actorController.construct(actorBuilder);
//todo:檢驗創(chuàng)建結(jié)果
String type = actor.getType();
System.out.println(type+"的外觀");
System.out.println("性別:"+actor.getSex());
System.out.println("面容:"+actor.getFace());
System.out.println("服裝:"+actor.getCostume());
System.out.println("發(fā)型:"+actor.getHairstyle());
}
}
十、打印結(jié)果
構(gòu)建不同的角色戚绕,我們只需要改變XML中的className節(jié)點纹坐,改變反射構(gòu)建的運行時類的類名。不同的類名舞丛,構(gòu)建不同的角色恰画。
3 總結(jié)
3.1 建造者模式優(yōu)點
- 在建造者模式中宾茂,客戶端不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié),將產(chǎn)品本身與產(chǎn)品的創(chuàng)建過程解耦拴还,使得相同的創(chuàng)建過程可以創(chuàng)建不同的產(chǎn)品對象跨晴。
- 每一個具體的建造者都相對獨立,而與其他的具體建造者無關(guān)片林,因此可以很方便地替換具體建造者或者添加新的具體建造者端盆,用戶使用不同的具體建造者即可得到不同的產(chǎn)品對象。由于指揮者類針對抽象建造者編程费封,增加新的具體建造者無需修改原有類庫的代碼焕妙,系統(tǒng)擴展方便,符合開閉原則弓摘。
- 可以更加精細(xì)地控制產(chǎn)品的創(chuàng)建過程焚鹊。將復(fù)雜產(chǎn)品的創(chuàng)建步驟分解在不同的方法中,使得創(chuàng)建過程更加清晰韧献,也更加方便使用程序來控制創(chuàng)建過程末患。
3.2 建造者模式缺點
- 建造者模式所創(chuàng)建的產(chǎn)品一般具有較多的共同點,其組成部分相似锤窑,如果產(chǎn)品之間的差異性很大璧针,例如很多組成部分都不相同,不適合使用建造者模式渊啰,因此其使用的范圍受到一定限制探橱。
- 如果產(chǎn)品的內(nèi)部變化復(fù)雜,可能會導(dǎo)致需要定義很多具體建造者類來實現(xiàn)這種變化绘证,到這系統(tǒng)變得很龐大隧膏,增加系統(tǒng)的理解難度和運行成本。
3.3 建造者模式適用環(huán)境
- 需要生成的產(chǎn)品對象有復(fù)雜的內(nèi)部結(jié)構(gòu)嚷那,這些產(chǎn)品對象通常包含多個成員變量私植。
- 需要生成的產(chǎn)品對象的屬性相互依賴,需要指定其生成順序车酣。
- 對象的創(chuàng)建過程獨立于創(chuàng)建該對象的類曲稼。在建造者模式中通過引入指揮者類將創(chuàng)建過程封裝在指揮者類中,而不再建造者類和客戶類中湖员。
- 隔離復(fù)雜對象的創(chuàng)建和使用贫悄,并使得相同的創(chuàng)建過程可以創(chuàng)建不同的產(chǎn)品。