設(shè)計(jì)模式系列 — 建造者模式

點(diǎn)贊再看抄邀,養(yǎng)成習(xí)慣,公眾號(hào)搜一搜【一角錢技術(shù)】關(guān)注更多原創(chuàng)技術(shù)文章昼榛。
本文 GitHub org_hejianhui/JavaStudy 已收錄境肾,有我的系列文章。

前言

23種設(shè)計(jì)模式快速記憶的請看上面第一篇胆屿,本篇和大家一起來學(xué)習(xí)建造者模式相關(guān)內(nèi)容奥喻。


模式定義

將一個(gè)復(fù)雜對象的創(chuàng)建與他的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示非迹。

  1. 用戶只需要給出指定復(fù)雜對象的類型和內(nèi)容环鲤;
  2. 建造者模式負(fù)責(zé)按順序創(chuàng)建復(fù)雜對象(把內(nèi)部的建造過程和細(xì)節(jié)隱藏起來)

解決的問題

  1. 降低創(chuàng)建復(fù)雜對象的復(fù)雜度
  2. 隔離了創(chuàng)建對象的構(gòu)建過程 & 表示

從而:

  • 方便用戶創(chuàng)建復(fù)雜的對象(不需要知道實(shí)現(xiàn)過程)
  • 代碼復(fù)用性 & 封裝性(將對象構(gòu)建過程和細(xì)節(jié)進(jìn)行封裝 & 復(fù)用)

模式組成

  1. 指揮者(Director)直接和客戶(Client)進(jìn)行需求溝通;
  2. 溝通后指揮者將客戶創(chuàng)建產(chǎn)品的需求劃分為各個(gè)部件的建造請求(Builder)憎兽;
  3. 將各個(gè)部件的建造請求委派到具體的建造者(ConcreteBuilder)冷离;
  4. 各個(gè)具體建造者負(fù)責(zé)進(jìn)行產(chǎn)品部件的構(gòu)建;
  5. 最終構(gòu)建成具體產(chǎn)品(Product)唇兑。

實(shí)例說明

實(shí)例概況

  • 背景
    小張希望去中關(guān)村買一臺(tái)組裝的臺(tái)式主機(jī)
  • 過程
  1. 中關(guān)村老板(Diretor)和小張(Client)進(jìn)行需求溝通(買來打游戲酒朵?學(xué)習(xí)?看片扎附?)
  2. 了解需求后蔫耽,電腦城老板將小張需要的主機(jī)劃分為各個(gè)部件(Builder)的建造請求(CPU、主板......)
  3. 指揮裝機(jī)人員(ConcreteBuilder)去構(gòu)建組件留夜;
  4. 將組件組裝起來成小張需要的電腦(Product)

使用步驟

步驟1:定義具體產(chǎn)品類(Product):電腦

class Computer{

    //電腦組件的集合
    private List<String> parts = new ArrayList<String>();

    //用于將組件組裝到電腦里
    public void Add(String part){
        parts.add(part);
    }

    public void Show(){
        for (int i = 0;i<parts.size();i++){
            System.out.println("組件" + parts.get(i) + "裝好了");
        }
        System.out.println("電腦組裝完成匙铡,請驗(yàn)收");
    }
}

步驟2:定義組裝的過程(Builder):組裝電腦的過程

abstract class Builder {

    //第一步:裝CPU
    //聲明為抽象方法,具體由子類實(shí)現(xiàn)
    public abstract void  BuildCPU();

    //第二步:裝主板
    //聲明為抽象方法碍粥,具體由子類實(shí)現(xiàn)
    public abstract void BuildMainboard();

    //第三步:裝硬盤
    //聲明為抽象方法鳖眼,具體由子類實(shí)現(xiàn)
    public abstract void BuildHD();

    //返回產(chǎn)品的方法:獲得組裝好的電腦
    public abstract Computer GetComputer();
}

步驟3: 中關(guān)村老板委派任務(wù)給裝機(jī)人員(Director)

class Director{
    //指揮裝機(jī)人員組裝電腦
    public void Construct(Builder builder){
        builder. BuildCPU();
        builder.BuildMainboard();
        builder.BuildHD();
    }
}

步驟4: 創(chuàng)建具體的建造者(ConcreteBuilder):裝機(jī)人員

class ConcreteBuilder extends Builder{
    //創(chuàng)建產(chǎn)品實(shí)例
    Computer computer = new Computer();

    //組裝產(chǎn)品
    @Override
    public void  BuildCPU(){
        computer.Add("組裝CPU");
    }

    @Override
    public void  BuildMainboard() {
        computer.Add("組裝主板");
    }

    @Override
    public void  BuildHD() {
        computer.Add("組裝主板");
    }

    //返回組裝成功的電腦
    @Override
    public  Computer GetComputer(){
        return computer;
    }
}

步驟5:客戶端調(diào)用-小張到電腦城找老板買電腦

public class BuilderPattern<builder> {

    public static void main(String[] args) {
        // 步驟5:客戶端調(diào)用-小張到電腦城找老板買電腦

        //逛了很久終于發(fā)現(xiàn)一家合適的電腦店
        //找到該店的老板和裝機(jī)人員
        Director director = new Director();

        Builder builder = new ConcreteBuilder();

        //溝通需求后,老板叫裝機(jī)人員去裝電腦
        director.Construct(builder);

        //裝完后嚼摩,組裝人員搬來組裝好的電腦
        Computer computer = builder.GetComputer();
        //組裝人員展示電腦給小張看
        computer.Show();
    }
}

輸出結(jié)果

組件CPU裝好了
組件主板裝好了
組件硬盤裝好了
電腦組裝完成钦讳,請驗(yàn)收

優(yōu)點(diǎn)

  1. 良好的封裝性:建造者對客戶端屏蔽了產(chǎn)品內(nèi)部組成的細(xì)節(jié),客戶端不用關(guān)心每一個(gè)具體的產(chǎn)品內(nèi)部是如何實(shí)現(xiàn)的枕面。
  2. 符合開閉原則
  3. 便于控制細(xì)節(jié)風(fēng)險(xiǎn):由于建造者是相互獨(dú)立的愿卒,因此可以對建造過程逐步細(xì)化,而不對其他的模塊產(chǎn)生任何影響潮秘。

每一個(gè)具體建造者都相對獨(dú)立琼开,而與其他的具體建造者無關(guān),因此可以很方便地替換具體建造者或增加新的具體建造者枕荞,用戶使用不同的具體建造者即可得到不同的產(chǎn)品對象柜候。

缺點(diǎn)

  1. 建造者模式所創(chuàng)建的產(chǎn)品一般具有較多的共同點(diǎn)搞动,其組成部分相似;如果產(chǎn)品之間的差異性很大渣刷,則不適合使用建造者模式鹦肿,因此其使用范圍受到一定的限制。
  2. 如果產(chǎn)品的內(nèi)部變化復(fù)雜辅柴,可能會(huì)導(dǎo)致需要定義很多具體建造者類來實(shí)現(xiàn)這種變化狮惜,導(dǎo)致系統(tǒng)變得很龐大。

應(yīng)用場景

  1. 需要生成的對象具有復(fù)雜的內(nèi)部結(jié)構(gòu)
  2. 需要生成的對象內(nèi)部屬性本身相互依賴
  3. 與不可變對象配合使用

與工廠方法模式的區(qū)別

建造者模式最主要的功能是基本方法的調(diào)用順序安排碌识,基本方法已經(jīng)實(shí)現(xiàn),我們可以理解為零件的裝配虱而,順序不同產(chǎn)生的對象也不同筏餐;而工廠方法的注重點(diǎn)是創(chuàng)建,創(chuàng)建零件是其主要職責(zé)牡拇,不關(guān)心組裝順序魁瞪。

源碼中的應(yīng)用

# jdk
java.lang.StringBuilder
# Spring源碼
org.springframework.web.servlet.mvc.method.RequestMappingInfo
org.springframework.beans.factory.support.BeanDefinitionBuilder
......

StringBuilder源碼分析

在jdk中StringBuilder類的實(shí)現(xiàn)中,采用建造者模式的思想惠呼。具體分析如下:

StringBuilder類繼承自AbstractStringBuilder葛菇,而AbstractStringBuilder實(shí)現(xiàn)了Appendable接口周循。AbstractStringBuilder雖然是一個(gè)抽象類,但是它實(shí)現(xiàn)了Appendable接口中的各個(gè)append()方法,因此在這里Appendable接口是一個(gè)抽象建造者椎侠,而AbstractStringBuilder是建造者,只是不能實(shí)例化昔头。對于StringBuilder類等孵,它既充當(dāng)了指揮者角色,同時(shí)充當(dāng)了具體的建造者矫付,建造方法的具體實(shí)現(xiàn)是由AbstractStringBuilder完成凯沪,StringBuilder繼承了AbstractStringBuilder

Appendable接口

public interface Appendable {
    Appendable append(CharSequence csq) throws IOException;
    Appendable append(CharSequence csq, int start, int end) throws IOException;
    Appendable append(char c) throws IOException;
}

AbstractStringBuilder類

abstract class AbstractStringBuilder implements Appendable, CharSequence {
 
    char[] value;//The value is used for character storage.
    int count;//The count is the number of characters used.

    AbstractStringBuilder() { }

    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }

    public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

    private AbstractStringBuilder appendNull() {
        int c = count;
        ensureCapacityInternal(c + 4);
        final char[] value = this.value;
        value[c++] = 'n';
        value[c++] = 'u';
        value[c++] = 'l';
        value[c++] = 'l';
        count = c;
        return this;
    }

    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) {
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }

    public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
        if (srcBegin < 0) {
            throw new StringIndexOutOfBoundsException(srcBegin);
        }
        if (srcEnd > value.length) {
            throw new StringIndexOutOfBoundsException(srcEnd);
        }
        if (srcBegin > srcEnd) {
            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
        }
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    }
    // 此次省略......

}

StringBuilder類:

public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence {
    //雖說是重寫买优,但還是調(diào)用的AbstractStringBuilder方法
   @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
}

PS:以上代碼提交在 Githubhttps://github.com/Niuh-Study/niuh-designpatterns.git

文章持續(xù)更新妨马,可以公眾號(hào)搜一搜「 一角錢技術(shù) 」第一時(shí)間閱讀。
本文 GitHub org_hejianhui/JavaStudy 已經(jīng)收錄杀赢,歡迎 Star烘跺。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市葵陵,隨后出現(xiàn)的幾起案子液荸,更是在濱河造成了極大的恐慌,老刑警劉巖脱篙,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件娇钱,死亡現(xiàn)場離奇詭異伤柄,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)文搂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進(jìn)店門适刀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人煤蹭,你說我怎么就攤上這事笔喉。” “怎么了硝皂?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵常挚,是天一觀的道長。 經(jīng)常有香客問我稽物,道長奄毡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任贝或,我火速辦了婚禮吼过,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘咪奖。我一直安慰自己盗忱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布羊赵。 她就那樣靜靜地躺著趟佃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪慷垮。 梳的紋絲不亂的頭發(fā)上揖闸,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天,我揣著相機(jī)與錄音料身,去河邊找鬼汤纸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛芹血,可吹牛的內(nèi)容都是我干的贮泞。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼幔烛,長吁一口氣:“原來是場噩夢啊……” “哼啃擦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起饿悬,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤令蛉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體珠叔,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蝎宇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了祷安。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姥芥。...
    茶點(diǎn)故事閱讀 40,680評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖汇鞭,靈堂內(nèi)的尸體忽然破棺而出凉唐,到底是詐尸還是另有隱情,我是刑警寧澤霍骄,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布台囱,位于F島的核電站,受9級(jí)特大地震影響读整,放射性物質(zhì)發(fā)生泄漏玄坦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一绘沉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧豺总,春花似錦车伞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至表伦,卻和暖如春谦去,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蹦哼。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工鳄哭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人纲熏。 一個(gè)月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓妆丘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親局劲。 傳聞我的和親對象是個(gè)殘疾皇子勺拣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評論 2 361