Java設(shè)計模式-創(chuàng)建型模式-建造者模式

此系列文章為清華大學(xué)出版社出版劉偉編著《Java設(shè)計模式》的學(xué)習(xí)筆記爬虱。

>>全部23種設(shè)計模式<<

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個角色

  1. AbstractBuilder(抽象建造者):它為創(chuàng)建一個產(chǎn)品對象的各個部件指定抽象接口虏两,在該接口中一般聲明兩類方法,一類方法是buildPartX()世剖,它們用于創(chuàng)建復(fù)雜對象的各個部件定罢;另一類方法是getResult(),它們用于返回復(fù)雜對象旁瘫。AbstractBuilder既可以是抽象類祖凫,也可以是接口。
  2. ConcreteBuilder(具體建造者):它實現(xiàn)了AbstractBuilder中的抽象方法酬凳,實現(xiàn)各個部件的具體構(gòu)造和裝配方法惠况。定義并明確所創(chuàng)建的復(fù)雜對象,還可以提供一個方法返回創(chuàng)建好的復(fù)雜產(chǎn)品對象宁仔。
  3. Product(產(chǎn)品):它是被構(gòu)建的復(fù)雜對象售滤,包含多個組成部件,具體建造者創(chuàng)建該產(chǎn)品的內(nèi)部表示并定義它的裝配過程台诗。
  4. 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)

項目結(jié)構(gòu).png

三技俐、抽象建造者

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)建不同的角色恰画。

天使角色.png
英雄角色.png
惡魔角色.png

3 總結(jié)

3.1 建造者模式優(yōu)點

  1. 在建造者模式中宾茂,客戶端不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié),將產(chǎn)品本身與產(chǎn)品的創(chuàng)建過程解耦拴还,使得相同的創(chuàng)建過程可以創(chuàng)建不同的產(chǎn)品對象跨晴。
  2. 每一個具體的建造者都相對獨立,而與其他的具體建造者無關(guān)片林,因此可以很方便地替換具體建造者或者添加新的具體建造者端盆,用戶使用不同的具體建造者即可得到不同的產(chǎn)品對象。由于指揮者類針對抽象建造者編程费封,增加新的具體建造者無需修改原有類庫的代碼焕妙,系統(tǒng)擴展方便,符合開閉原則弓摘。
  3. 可以更加精細(xì)地控制產(chǎn)品的創(chuàng)建過程焚鹊。將復(fù)雜產(chǎn)品的創(chuàng)建步驟分解在不同的方法中,使得創(chuàng)建過程更加清晰韧献,也更加方便使用程序來控制創(chuàng)建過程末患。

3.2 建造者模式缺點

  1. 建造者模式所創(chuàng)建的產(chǎn)品一般具有較多的共同點,其組成部分相似锤窑,如果產(chǎn)品之間的差異性很大璧针,例如很多組成部分都不相同,不適合使用建造者模式渊啰,因此其使用的范圍受到一定限制探橱。
  2. 如果產(chǎn)品的內(nèi)部變化復(fù)雜,可能會導(dǎo)致需要定義很多具體建造者類來實現(xiàn)這種變化绘证,到這系統(tǒng)變得很龐大隧膏,增加系統(tǒng)的理解難度和運行成本。

3.3 建造者模式適用環(huán)境

  1. 需要生成的產(chǎn)品對象有復(fù)雜的內(nèi)部結(jié)構(gòu)嚷那,這些產(chǎn)品對象通常包含多個成員變量私植。
  2. 需要生成的產(chǎn)品對象的屬性相互依賴,需要指定其生成順序车酣。
  3. 對象的創(chuàng)建過程獨立于創(chuàng)建該對象的類曲稼。在建造者模式中通過引入指揮者類將創(chuàng)建過程封裝在指揮者類中,而不再建造者類和客戶類中湖员。
  4. 隔離復(fù)雜對象的創(chuàng)建和使用贫悄,并使得相同的創(chuàng)建過程可以創(chuàng)建不同的產(chǎn)品。

>>全部23種設(shè)計模式<<

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末娘摔,一起剝皮案震驚了整個濱河市窄坦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖鸭津,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件定躏,死亡現(xiàn)場離奇詭異搪缨,居然都是意外死亡掩蛤,警方通過查閱死者的電腦和手機萄涯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闻书,“玉大人名斟,你說我怎么就攤上這事∑敲迹” “怎么了砰盐?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長坑律。 經(jīng)常有香客問我岩梳,道長,這世上最難降的妖魔是什么晃择? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任冀值,我火速辦了婚禮,結(jié)果婚禮上藕各,老公的妹妹穿的比我還像新娘。我一直安慰自己焦除,他們只是感情好激况,可當(dāng)我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著膘魄,像睡著了一般乌逐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上创葡,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天浙踢,我揣著相機與錄音,去河邊找鬼灿渴。 笑死洛波,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的骚露。 我是一名探鬼主播蹬挤,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼棘幸!你這毒婦竟也來了焰扳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吨悍,沒想到半個月后扫茅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡育瓜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年葫隙,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片爆雹。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡停蕉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出钙态,到底是詐尸還是另有隱情慧起,我是刑警寧澤,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布册倒,位于F島的核電站蚓挤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏驻子。R本人自食惡果不足惜灿意,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望崇呵。 院中可真熱鬧缤剧,春花似錦、人聲如沸域慷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽犹褒。三九已至抵窒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間叠骑,已是汗流浹背李皇。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留宙枷,地道東北人掉房。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像慰丛,于是被迫代替她去往敵國和親圃阳。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內(nèi)容