文章首發(fā):
創(chuàng)建型模式:建造者模式
成都創(chuàng)新互聯(lián)擁有網(wǎng)站維護技術(shù)和項目管理團隊,建立的售前、實施和售后服務(wù)體系,為客戶提供定制化的成都做網(wǎng)站、成都網(wǎng)站建設(shè)、成都外貿(mào)網(wǎng)站建設(shè)、網(wǎng)站維護、德陽服務(wù)器托管解決方案。為客戶網(wǎng)站安全和日常運維提供整體管家式外包優(yōu)質(zhì)服務(wù)。我們的網(wǎng)站維護服務(wù)覆蓋集團企業(yè)、上市公司、外企網(wǎng)站、商城網(wǎng)站建設(shè)、政府網(wǎng)站等各類型客戶群體,為全球上1000家企業(yè)提供全方位網(wǎng)站維護、服務(wù)器維護解決方案。
五大創(chuàng)建型模式之四:建造者模式。
姓名:建造者模式
英文名:Builder Pattern
價值觀:專治丟三落四
個人介紹:
Separate the construction of a complex object from its representation so that the same construction process can create different representations.
將一個復(fù)雜對象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。
(來自《設(shè)計模式之禪》)
今天給大家介紹的是建造者模式。建造者模式的使用場景是:創(chuàng)建復(fù)雜的對象。什么才能算復(fù)雜對象?如果一個對象只需要通過 new XXX() 的方式創(chuàng)建,那就算是一個簡單對象;如果需要 new XXX(),并且還要設(shè)置很多屬性,那這種就可以稱為復(fù)雜對象了,因為它的構(gòu)建過程比較復(fù)雜。采用建造者模式,可以把這個復(fù)雜的構(gòu)建過程抽離開,使它不依賴創(chuàng)建者。下面我們通過故事來講解。
還記得小時候剛開始學(xué)煮湯,不了解怎么煮,第一次是煮了板栗排骨湯,因為板栗比較難熟,所以是跟排骨一起下鍋,然后煮了 40 分鐘,加了鹽就可以出鍋啦。第二次煮了冬瓜排骨湯,不懂得冬瓜容易熟,就和排骨一起下鍋,煮了也差不多 40 分鐘,下了鹽和香菜,發(fā)現(xiàn)怎么這湯有點濃,冬瓜咋都不見了(全都煮透了),我媽看到這鍋湯,才跟我說冬瓜容易熟,得先熬排骨,再放冬瓜進去煮。那時才發(fā)現(xiàn)熬湯特么還有這差異。
上面是背景哈,接下來我們來實現(xiàn)這個煲湯過程,冬瓜排骨湯是先加排骨,熬制 30 分鐘,加冬瓜,熬制 18 分鐘,加鹽加香菜;板栗排骨湯是先加排骨和板栗,熬制 40 分鐘,再加鹽。這湯都需要加肉、加菜、熬制、加配料。我們使用下面代碼實現(xiàn)這個煲冬瓜排骨湯和板栗排骨湯的過程。
public class NoBuilderTest {
public static void main(String[] args) {
// 熬制冬瓜排骨湯
DongGuaPaiGuSoup dongGuaPaiGuSoup = new DongGuaPaiGuSoup();
// 加排骨
dongGuaPaiGuSoup.addMeat();
// 熬制 30 分鐘
dongGuaPaiGuSoup.waitMinute(30);
// 加冬瓜
dongGuaPaiGuSoup.addVegetables();
// 熬制 10 分鐘
dongGuaPaiGuSoup.waitMinute(10);
// 加鹽加香菜
dongGuaPaiGuSoup.addIngredients();
// 熬制板栗排骨湯
BanLiPaiGuSoup banLiPaiGuSoup = new BanLiPaiGuSoup();
// 加排骨
banLiPaiGuSoup.addMeat();
// 加板栗
banLiPaiGuSoup.addVegetables();
// 熬制 40 分鐘
banLiPaiGuSoup.waitMinute(40);
// 加鹽
banLiPaiGuSoup.addIngredients();
}
}
/**
* 煲湯接口
*/
interface Soup {
/** 加肉 */
void addMeat();
/** 加菜 */
void addVegetables();
/** 熬制 */
void waitMinute(int minute);
/** 加配料 */
void addIngredients();
}
/**
* 冬瓜排骨湯
*/
class DongGuaPaiGuSoup implements Soup {
@Override
public void addMeat() {
System.out.println("加排骨");
}
@Override
public void addVegetables() {
System.out.println("加冬瓜");
}
@Override
public void waitMinute(int minute) {
System.out.println("熬制 " + minute + " 分鐘");
}
@Override
public void addIngredients() {
System.out.println("加鹽、加香菜");
}
}
/**
* 板栗排骨湯
*/
class BanLiPaiGuSoup implements Soup {
@Override
public void addMeat() {
System.out.println("加排骨");
}
@Override
public void addVegetables() {
System.out.println("加板栗");
}
@Override
public void waitMinute(int minute) {
System.out.println("熬制 " + minute + " 分鐘");
}
@Override
public void addIngredients() {
System.out.println("加鹽");
}
}
上面代碼簡單實現(xiàn)了煲冬瓜排骨湯和板栗排骨湯。煲湯我們要關(guān)注的點是:各操作的順序,是先加肉先煮再加菜,還是肉和菜一起放進鍋煮。上面代碼中,這個過程是誰控制的?是煲湯的人,所以順序由煲湯的人決定,甚至有可能忘記放配料啥的,這樣子的湯就味道不夠好。那怎么去解決這些問題?
我們通過建造者模式可以解決上面的 2 個問題:煲湯順序問題和忘記加配料這種丟三落四行為。我們將這個煲湯順序從煲湯者分離開來,讓煲湯者只需要決定煲什么湯就好,讓建造者來保證煲湯順序問題和防止漏加配料。
我們用一個 SoupBuilder 來規(guī)范化煲湯過程,方法 buildSoup 給實現(xiàn)者提供一個設(shè)置煲湯順序的地方。因為冬瓜排骨湯和板栗排骨湯熬制的過程不一樣,所以分別用 DongGuaPaiGuSoupBuilder 和 BanLiPaiGuSoupBuilder 來具體實現(xiàn)冬瓜排骨湯和板栗排骨湯的熬制過程,也就是消除熬制過程和煲湯者的依賴關(guān)系。 Director 則相當于一個菜單,提供為熬湯者來選擇熬什么湯。具體代碼如下所示。
public class BuilderTest {
public static void main(String[] args) {
Director director = new Director();
// 熬制冬瓜排骨湯
director.buildDongGuaPaiGuSoup();
// 熬制板栗排骨湯
director.buildBanLiPaiGuSoup();
}
}
/**
* 煲湯建造接口
*/
interface SoupBuilder {
void buildSoup();
Soup getSoup();
}
/**
* 冬瓜排骨湯建造者
*/
class DongGuaPaiGuSoupBuilder implements SoupBuilder {
private DongGuaPaiGuSoup dongGuaPaiGuSoup = new DongGuaPaiGuSoup();
@Override
public void buildSoup() {
// 加排骨
dongGuaPaiGuSoup.addMeat();
// 熬制 30 分鐘
dongGuaPaiGuSoup.waitMinute(30);
// 加冬瓜
dongGuaPaiGuSoup.addVegetables();
// 熬制 10 分鐘
dongGuaPaiGuSoup.waitMinute(10);
// 加鹽加香菜
dongGuaPaiGuSoup.addIngredients();
}
@Override
public Soup getSoup() {
return dongGuaPaiGuSoup;
}
}
/**
* 板栗排骨湯建造者
*/
class BanLiPaiGuSoupBuilder implements SoupBuilder {
BanLiPaiGuSoup banLiPaiGuSoup = new BanLiPaiGuSoup();
@Override
public void buildSoup() {
// 加排骨
banLiPaiGuSoup.addMeat();
// 加板栗
banLiPaiGuSoup.addVegetables();
// 熬制 40 分鐘
banLiPaiGuSoup.waitMinute(40);
// 加鹽
banLiPaiGuSoup.addIngredients();
}
@Override
public Soup getSoup() {
return banLiPaiGuSoup;
}
}
/**
* 生產(chǎn)方
*/
class Director {
private DongGuaPaiGuSoupBuilder dongGuaPaiGuSoupBuilder = new DongGuaPaiGuSoupBuilder();
private BanLiPaiGuSoupBuilder banLiPaiGuSoupBuilder = new BanLiPaiGuSoupBuilder();
/**
* 熬制冬瓜排骨湯
*/
public DongGuaPaiGuSoup buildDongGuaPaiGuSoup() {
dongGuaPaiGuSoupBuilder.buildSoup();
return (DongGuaPaiGuSoup) dongGuaPaiGuSoupBuilder.getSoup();
}
/**
* 熬制板栗排骨湯
*/
public BanLiPaiGuSoup buildBanLiPaiGuSoup() {
banLiPaiGuSoupBuilder.buildSoup();
return (BanLiPaiGuSoup) banLiPaiGuSoupBuilder.getSoup();
}
}
通過用建造者實現(xiàn),是不是保證了熬制湯的順序并且一定會加夠料?感受一下其中的奧秘吧。
代碼:
Builder Pattern
通過建造者模式,可以把本來強依賴的東西解綁,不僅僅解決依賴問題,還提高了封裝性,讓使用者不用明白內(nèi)部的細節(jié),用上面的例子說就熬湯不用關(guān)心怎么熬制的過程,就像我們想喝冬瓜排骨湯,告訴媽媽,媽媽熬制,我們并不知道是怎么熬制的。
推薦閱讀
單一職責原則(方法:修改名字還是密碼?接口:洗碗、買菜還是倒垃圾?類:注冊、登錄和注銷)
里氏替換原則(我兒來自新東方烹飪)
依賴倒置原則(摳門的飯店老板)
接口隔離原則(小伙子的作坊)
迪米特法則(手機上看電子書)
開閉原則(社保這點事)
創(chuàng)建型模式:單例模式(小明就只有 1 輛車)
創(chuàng)建型模式:工廠方法(小明家的車庫)
創(chuàng)建型模式:抽象工廠(寶馬車就得用寶馬輪胎和寶馬方向盤)
設(shè)計模式文章會持續(xù)更新,歡迎關(guān)注公眾號,第一時間獲取文章推送。
公眾號后臺回復(fù)『大禮包』獲取 Java、Python、IOS 等教程
加個人微信備注『教程』獲取架構(gòu)師、機器學(xué)習(xí)等教程