java設(shè)計模式(三)建造者模式

開篇廢話

周末因懶的原因饲趋,停更了兩天果覆。今天主要研究建造者模式。暢游各大博客網(wǎng)站横侦,推薦一篇關(guān)于建造者模式的博客http://blog.csdn.net/self_study/article/details/51707029,簡單明了绰姻,條例清晰枉侧。如果看不懂我寫的,可以嘗試去看看別人的狂芋。榨馁。。

什么是建造者模式

講建造者模式银酗,這里要提取幾個關(guān)鍵詞:

  • 復(fù)雜對象: 這里簡單的體現(xiàn)可以理解為辆影,這里對象的成員數(shù)量很多,所以它很復(fù)雜黍特。
  • 構(gòu)建過程: 對成員對象進行賦值,業(yè)務(wù)邏輯生成等一系列使之成為一個成熟的對象锯蛀。
  • 表示: 返回這個對象灭衷。
  • 構(gòu)建過程與表示分離: 一般來說,對成員對象的賦值通常在構(gòu)造函數(shù)中旁涤,這種方式除了不夠靈活之外翔曲,還有就是如果成員對象過多,因為構(gòu)造函數(shù)就會顯得十分臃腫劈愚。所以瞳遍,我們可以將構(gòu)建過程(簡單的理解為賦值,這里不嚴謹菌羽,只是為了幫助理解概念)封裝成獨立的方法掠械,然后再返回整個需要的實例對象。

那么建造者模式:就是為了解決生成復(fù)雜對象的問題,主要通過將構(gòu)建過程與表示分離的方法猾蒂。


如何實現(xiàn)建造者模式

理解了建造者模式后均唉,我們來實現(xiàn)它,就相對簡單了肚菠。
首先盜圖一張:


建造者模式類圖

可以看到舔箭,這里呈現(xiàn)了四個類,有時候?qū)嶋H開發(fā)中會將這個模型進行簡化蚊逢,我們這里先學(xué)習(xí)理論层扶,因為實際操作時,靈活變動太多了烙荷,不適合講解怒医。但是萬變不離其宗,核心思想就是這些奢讨。

  • Product稚叹,都稱這個為產(chǎn)品類,說白了就是我需要生成的類拿诸。
  • Builder類扒袖,這是一個抽象類或者接口,用來規(guī)定構(gòu)建對象的所需方法亩码。
  • ConcreteBuilder 對builder的抽象方法的實現(xiàn)類季率,通常對其優(yōu)化為product的內(nèi)部類,從而省掉builder的定義描沟。常見的有 AlertDialog.Builder 飒泻。
  • Director 調(diào)用concrete的方法的類。一般來說吏廉,使用的建造者模式時泞遗,這個也給省了。

我們先來看一個完整版的席覆。以造汽車為例史辙,現(xiàn)在我們需要一輛,有四個輪子佩伤,有四個座椅聊倔,有防風(fēng)玻璃,有方向盤生巡,發(fā)動機等等(這里只列舉這幾樣)耙蔑。

  • 首先我們先看Product類,就是我們要生成的復(fù)雜對象孤荣,這里就是指小汽車甸陌。
public class Car {
    List<String> tyres = new ArrayList<>();
    List<String> carMounts = new ArrayList<>();
    String glass;
    String steeringWheel;
    String engine;

    public void setTyres(String tyres) {
        this.tyres.add(tyres);
    }

    public void setCarMounts(String carMounts) {
        this.carMounts.add(carMounts);
    }

    public void setGlass(String glass) {
        this.glass = glass;
    }

    public void setSteeringWheel(String steeringWheel) {
        this.steeringWheel = steeringWheel;
    }

    public void setEngine(String engine) {
        this.engine = engine;
    }

    public List<String> getTyres() {
        return tyres;
    }

    public List<String> getCarMounts() {
        return carMounts;
    }

    public String getGlass() {
        return glass;
    }

    public String getSteeringWheel() {
        return steeringWheel;
    }

    public String getEngine() {
        return engine;
    }

}

這里可以看到小汽車有五個屬性须揣,然后通過setter和getter方法來對屬性賦值。

  • 然后我們先看Builder類邀层,這是一個抽象類或者接口返敬,具體使用看情況而定,我今天想用接口表示:
public interface Builder {
    public void buildeTyre(int wheelNumber);
    public void buildCarMounts(int carMountsNumber);
    public void buildGalss();
    public void buildSteeringWheel();
    public void buildEngine();
    public Car build();
}

這里定義了五個造小汽車零件的方法寥院,和一個生成對象方法劲赠,因為你不能只造這些零件,還需要把這些零件給組裝起來啊秸谢。

  • 緊接著我們需要ConcreteBuilder類來實現(xiàn)這些方法啊凛澎,畢竟我們是實干家,不能光說不做估蹄!concretebuilder
public class CarBuilder implements Builder {
    
    Car car = new  Car();
    @Override
    public void buildeTyre(int wheelNumber) {
        for (int i = 0; i < wheelNumber; i++) {
            car.setTyres("已經(jīng)造了" + i + "輪子了");
        }
    }

    @Override
    public void buildCarMounts(int carMountsNumber) {
        for (int i = 0; i < carMountsNumber; i++) {
            car.setCarMounts("已經(jīng)造了" + i + "椅子了");
        }
    }

    @Override
    public void buildGalss() {
        car.setGlass("防風(fēng)玻璃做好了");
    }

    @Override
    public void buildSteeringWheel() {
        car.setSteeringWheel("方向盤做好了");
    }

    @Override
    public void buildEngine() {
        car.setEngine("發(fā)動機已經(jīng)做好了");
    }

    @Override
    public Car build() {
        return car;
    }

}

這里實現(xiàn)了builder接口塑煎,沒什么好說的,值得注意的就是臭蚁,在CarBuilder類中最铁,維護了一個Car對象,實現(xiàn)的所有方法中垮兑,去調(diào)用Car的set方法冷尉,最后build()方法中去返回這個對象。
此時此刻系枪,我們已經(jīng)能通過CarBuilder類去建造我們所需要的Car對象了雀哨。但是畢竟造汽車的工序是及其復(fù)雜了,因為我們這里只簡單的列舉了五項私爷,可能感覺還是比較輕松的雾棺,但是,如果有上百道工序的話衬浑,難道我們需要每次去造小車的時候都去調(diào)用這上百個方法嗎捌浩。顯然,這里構(gòu)建的過程嚎卫,我們希望是不透明的嘉栓,那么我們可以將這個步驟封裝起來,于是就有了Director類的出現(xiàn)拓诸。

  • 我們希望有一個技術(shù)總工來調(diào)度負責(zé)整個造車的過程。
public class AodiDirecter {
    private Builder builder;

    public AodiDirecter(Builder builder) {
        this.builder = builder;
    }

    public void construct() {
        builder.buildeTyre(4);
        builder.buildCarMounts(4);
        builder.buildEngine();
        builder.buildGalss();
        builder.buildSteeringWheel();
    }
}

這樣整個建造者模式就完成了麻昼,而我們需要一個奧迪車時奠支,只需要:

public class Buyer {
    public void buy() {
        Builder builder = new CarBuilder();
        AodiDirecter director = new AodiDirecter(builder);
        director.construct();
        Car aodiCar = builder.build();
    }
}

如果說,builder是產(chǎn)線工人們抚芦,AodiDirecter 就可以理解為奧迪工程師倍谜,工人們只知道制造器件迈螟,工程師來統(tǒng)籌設(shè)計造車的工序,安排下去后尔崔,工人們按命令執(zhí)行就行了答毫。而對于買家來說,我并看不到你是怎么造的季春,反正我只要結(jié)果就行了洗搂。


但是這樣往往會很麻煩,我們通常會對其進行精簡優(yōu)化载弄。

看上面對建造者定義了四個功能模塊耘拇,在實現(xiàn)過程中,是不是有一些宇攻,何必多此一舉的感覺惫叛。這里我盜用了別人的代碼,工作繁忙(太懶了)還請諒解逞刷。

public class Computer {
    private String CPU;
    private String GPU;
    private String memoryType;
    private int memorySize;
    private String storageType;
    private int storageSize;
    private String screenType;
    private float screenSize;
    private String OSType;

    public static class Builder {
        // Optional parameters - initialize with default values
        private String CPU = "inter-i3";
        private String GPU = "GTX-960";
        private String memoryType = "ddr3 1666MHz";
        private int memorySize = 8;//8GB
        private String storageType = "hdd";
        private int storageSize = 1024;//1TB
        private String screenType = "IPS";
        private float screenSize = 23.8f;
        private String OSType = "Windows 10";

        public Builder() {
        }

        public Builder setCPU(String CPU) {
            this.CPU = CPU;
            return this;
        }

        public Builder setGPU(String GPU) {
            this.GPU = GPU;
            return this;
        }
        public Builder setMemoryType(String memoryType) {
            this.memoryType = memoryType;
            return this;
        }

        public Builder setMemorySize(int memorySize) {
            this.memorySize = memorySize;
            return this;
        }
        public Builder setStorageType(String storageType) {
            this.storageType = storageType;
            return this;
        }

        public Builder setStorageSize(int storageSize) {
            this.storageSize = storageSize;
            return this;
        }

        public Builder setScreenType(String screenType) {
            this.screenType = screenType;
            return this;
        }
        public Builder setScreenSize(float screenSize) {
            this.screenSize = screenSize;
            return this;
        }

        public Builder setOSType(String OSType) {
            this.OSType = OSType;
            return this;
        }


        public Computer create() {
            return new Computer(this);
        }

    }

    private Computer(Builder builder) {
        CPU = builder.CPU;
        GPU = builder.GPU;
        memoryType = builder.memoryType;
        memorySize = builder.memorySize;
        storageType = builder.storageType;
        storageSize = builder.storageSize;
        screenType = builder.screenType;
        screenSize = builder.screenSize;
        OSType = builder.OSType;
    }
}

可以看到嘉涌,整個建造者模式就是一個類,然而這個類中存在一個靜態(tài)內(nèi)部類Builder夸浅,這個Builder仑最,其實對應(yīng)的是建造者模式中的concreteBuilder,Computer對應(yīng)的就是我們product题篷。去掉了Builder抽象類甩苛,因為直接實現(xiàn)了。同時還缺少了Directer猴仑,因為這里的設(shè)計是暴露構(gòu)建細節(jié)村砂,所以就不需要了。
這種方式的好處就是葫笼,過程可控深啤。我們來看看調(diào)用。

Computer computer = new Computer.Builder()
        .setCPU("inter-skylake-i7")
        .setGPU("GTX-Titan")
        .setMemoryType("ddr4-2133MHz")
        .setMemorySize(16)
        .setStorageType("ssd")
        .setStorageSize(512)
        .setScreenType("IPS")
        .setScreenSize(28)
        .setOSType("Ubuntu/Window10")
        .create();

這是一種鏈?zhǔn)秸{(diào)用路星,也是我們常見的建造者模式溯街。我們可以對每個成員對象進行控制,從而得到我們想要的product洋丐。當(dāng)然呈昔,我們看回Computer類的實現(xiàn),發(fā)現(xiàn)友绝,每個成員對象都是有個默認值的堤尾。也就是說,你完全可以通過new Computer.Builder().create()來獲取默認的實例對象迁客,這基本就是Directer的功能郭宝。所以精簡下來辞槐,發(fā)現(xiàn)建造者模式還是挺實用的。
然而在大型的系統(tǒng)中粘室,精簡版的建造者模式榄檬,并不是很適用,因為它的針對性太強衔统,往往呈現(xiàn)出來的是同一種產(chǎn)品鹿榜,拓展性并不是很好,而我們使用原始版的話缰冤,可以對Directer進行拓展犬缨,可以更加多元靈活。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末棉浸,一起剝皮案震驚了整個濱河市怀薛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌迷郑,老刑警劉巖枝恋,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異嗡害,居然都是意外死亡焚碌,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門霸妹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來十电,“玉大人,你說我怎么就攤上這事叹螟【槁睿” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵罢绽,是天一觀的道長畏线。 經(jīng)常有香客問我,道長良价,這世上最難降的妖魔是什么寝殴? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮明垢,結(jié)果婚禮上蚣常,老公的妹妹穿的比我還像新娘。我一直安慰自己痊银,他們只是感情好史隆,可當(dāng)我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著曼验,像睡著了一般泌射。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鬓照,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天熔酷,我揣著相機與錄音,去河邊找鬼豺裆。 笑死拒秘,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的臭猜。 我是一名探鬼主播躺酒,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蔑歌!你這毒婦竟也來了羹应?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤次屠,失蹤者是張志新(化名)和其女友劉穎园匹,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體劫灶,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡裸违,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了本昏。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片供汛。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖涌穆,靈堂內(nèi)的尸體忽然破棺而出怔昨,到底是詐尸還是另有隱情,我是刑警寧澤蒲犬,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布朱监,位于F島的核電站,受9級特大地震影響原叮,放射性物質(zhì)發(fā)生泄漏赫编。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一奋隶、第九天 我趴在偏房一處隱蔽的房頂上張望擂送。 院中可真熱鬧,春花似錦唯欣、人聲如沸嘹吨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蟀拷。三九已至碰纬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間问芬,已是汗流浹背悦析。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留此衅,地道東北人强戴。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像挡鞍,于是被迫代替她去往敵國和親骑歹。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,619評論 2 354

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