為了防止被“殺”了祭天瓤檐,學(xué)點(diǎn)設(shè)計(jì)模式,并總結(jié)下還是有必要的排截。
一:理解
- 生成器模式又名建造模式嫌蚤,是一種對(duì)象構(gòu)建模式。
- 生成器用于構(gòu)建較為復(fù)雜的對(duì)象断傲,可以將復(fù)雜對(duì)象的創(chuàng)建過程抽象出來脱吱。
- 例如生產(chǎn)一臺(tái)電腦,涉及到主板认罩、CPU箱蝠、硬盤等零配件的生產(chǎn),不同型號(hào)的電腦又需要不同的配件垦垂。電腦對(duì)象的構(gòu)造較復(fù)雜宦搬,適合使用生成器模式。
- 生成器模式抽象出一臺(tái)機(jī)器劫拗,操作者無需了解電腦A和電腦B的具體組裝過程间校,他只需來到生產(chǎn)電腦A的機(jī)器或生產(chǎn)電腦B的機(jī)器前,按下生產(chǎn)按鈕即可页慷。
二:例子
你是個(gè)富二代憔足。
你家有個(gè)汽車廠,你帶著小金鏈子大金表酒繁,開心得當(dāng)著你的廠長(zhǎng)滓彰。
汽車廠的生產(chǎn)流程代碼是程序員小王寫的。
其中州袒,汽車類如下:
@Data
public class Car {
private Engine engine;
private Headlight headlight;
private Tyre tyre;
public Car() {
}
public Car(Engine engine, Headlight headlight, Tyre tyre) {
this.engine = engine;
this.headlight = headlight;
this.tyre = tyre;
}
public String toString() {
return engine.getName() + " " + headlight.getName() + " " + tyre.getName();
}
}
汽車類中包含三個(gè)屬性揭绑,分別是引擎,大燈和輪胎郎哭。
其中的零件類如下:
// 引擎類
@Data
public class Engine {
private String name;
}
// 大燈類
@Data
public class Headlight {
private String name;
}
// 輪胎類
@Data
public class Tyre {
private String name;
}
為了方便舉例洗做,零件類只包含一個(gè)name屬性弓叛。
你家的汽車廠生產(chǎn)很多不同品牌的汽車,不然也沒法在你高貴的朋友圈里裝X诚纸。
為了方便,這里只例舉寶馬車和奔馳車陈惰。
寶馬車和奔馳車的生產(chǎn)過程沒什么區(qū)別畦徘,只是它們對(duì)應(yīng)的零件不一樣。
寶馬車對(duì)應(yīng)的零件為:寶馬引擎抬闯,寶馬大燈井辆,米其林輪胎。
奔馳車對(duì)應(yīng)的零件為:奔馳引擎溶握,奔馳大燈杯缺,米其林輪胎。
對(duì)應(yīng)的零件類如下:
// 寶馬引擎
@Data
public class BMWEngine extends Engine {
public BMWEngine() {
setName("寶馬引擎");
}
}
// 寶馬大燈
@Data
public class BMWHeadlight extends Headlight {
public BMWHeadlight() {
setName("寶馬大燈");
}
}
// 奔馳引擎
@Data
public class BenzEngine extends Engine {
public BenzEngine() {
setName("奔馳引擎");
}
}
// 奔馳大燈
@Data
public class BenzHeadlight extends Headlight {
public BenzHeadlight() {
setName("奔馳大燈");
}
}
// 米其林輪胎
@Data
public class MichelinTyre extends Tyre {
public MichelinTyre() {
setName("米其林輪胎");
}
}
汽車廠員工各個(gè)都是精英睡榆,會(huì)所有品牌汽車的裝配方法萍肆。
例如,員工類中包含assembleBMW和assembleBenz方法胀屿。
兩個(gè)方法都新建一個(gè)Car對(duì)象塘揣,不同點(diǎn)在于傳入的零件不同。
public class Staff {
public Car assembleBMW() {
Car car = new Car(new BMWEngine(), new BMWHeadlight(), new MichelinTyre());
return car;
}
public Car assembleBenz() {
Car car = new Car(new BenzEngine(), new BenzHeadlight(), new MichelinTyre());
return car;
}
}
測(cè)試:
public class Client {
public static void main(String[] args) {
Staff staff = new Staff();
System.out.println(staff.assembleBMW());
System.out.println(staff.assembleBenz());
}
}
你發(fā)現(xiàn)宿崭,assembleBMW和assembleBenz方法類似亲铡,是否可以只對(duì)外暴露一個(gè)方法assembleCar,具體零件參數(shù)由調(diào)用方傳入葡兑。傳入寶馬車所需零件奖蔓,就生產(chǎn)寶馬車。
畢竟你是高富帥讹堤,保持代碼清晰優(yōu)雅是有需要的吆鹤,這樣逼格比較高。
于是就在你的淫威之下蜕劝,將代碼進(jìn)行了重構(gòu)檀头,的確變得清晰了一些。
public class Staff {
public Car assembleBMW(Engine engine, Headlight height, Tyre tyre) {
Car car = new Car(engine, height, tyre);
return car;
}
}
隨著你廠的業(yè)績(jī)?cè)絹碓胶冕妫銣?zhǔn)備生產(chǎn)更多品牌的汽車暑始。
小王發(fā)現(xiàn),如果需要生產(chǎn)新品牌的汽車婴削,有兩種方案:
- 在Staff類中增加方法廊镜,例如assembleAudi,需要修改Staff類唉俗。
- 讓調(diào)用方自己傳不同零件參數(shù)嗤朴。
然而配椭,你廠生產(chǎn)的汽車品牌實(shí)在太多了。
調(diào)用方經(jīng)常容易搞混雹姊,導(dǎo)致造出半寶馬半奔馳車股缸。
于是,你把程序員小王殺了祭天吱雏。
生活還得繼續(xù)敦姻,你找來程序員小菜,詢問是否有比較優(yōu)雅的方式完成這個(gè)需求歧杏。
小菜經(jīng)過思考镰惦,覺得可以使用生成器模式來完成這個(gè)優(yōu)雅的需求。
- 新建出制造汽車的機(jī)器犬绒,即生成器旺入。
- 不同品牌的汽車對(duì)應(yīng)不同的生成器,例如寶馬生成器凯力,奔馳生成器等茵瘾。
- 需要生產(chǎn)新品牌汽車,只需新建新的生成器沮协。
- 調(diào)用方無需知道每種車的生產(chǎn)方法龄捡,只需調(diào)用生成器的方法即可。
小菜上來就是一頓敲慷暂。
為了約束不同品牌汽車生成器的步驟聘殖,他首先新建了一個(gè)抽象類。
public abstract class Builder {
protected Car car;
public abstract void assembleEngine();
public abstract void assembleHeadlight();
public abstract void assembleTyre();
public Car createCar() {
this.car = new Car();
assembleEngine();
assembleHeadlight();
assembleTyre();
return car;
}
}
在這個(gè)抽象生成器中行瑞,包含Car屬性奸腺,和三個(gè)抽象方法,分別用于裝備引擎血久,裝配大燈和裝配輪胎突照。
此外有一個(gè)createCar方法,用于生成整輛汽車氧吐。
這里用到了模版方法模式讹蘑,將方法具體的實(shí)現(xiàn)延遲到子類。
小菜接著新建了寶馬汽車生成器和奔馳汽車生成器。
// 寶馬車生成器
public class BMWBuilder extends Builder {
@Override
public void assembleEngine() {
car.setEngine(new BMWEngine());
}
@Override
public void assembleHeadlight() {
car.setHeadlight(new BMWHeadlight());
}
@Override
public void assembleTyre() {
car.setTyre(new MichelinTyre());
}
}
// 奔馳車生成器
public class BenzBuilder extends Builder {
@Override
public void assembleEngine() {
car.setEngine(new BenzEngine());
}
@Override
public void assembleHeadlight() {
car.setHeadlight(new BenzHeadlight());
}
@Override
public void assembleTyre() {
car.setTyre(new MichelinTyre());
}
}
兩個(gè)生成器類,分別實(shí)現(xiàn)了抽象生成器中的抽象方法逛尚。
經(jīng)過重構(gòu)磅甩,員工類只需持有生成器對(duì)象和一個(gè)assembleCar方法扁达,該方法直接調(diào)用生成器的createCar方法。
@Data
public class StaffV2 {
private Builder builder;
public Car assembleCar() {
return builder.createCar();
}
}
員工類變得簡(jiǎn)單清晰介袜,好比就是員工只需來到生產(chǎn)汽車的機(jī)器面前痹兜,按下一個(gè)按鈕蛮粮,即可生產(chǎn)益缎。
測(cè)試:
public class ClientV2 {
public static void main(String[] args) {
StaffV2 staffV2 = new StaffV2();
BMWBuilder bmwBuilder = new BMWBuilder();
staffV2.setBuilder(bmwBuilder);
Car bmwCar = staffV2.assembleCar();
System.out.println(bmwCar);
BenzBuilder benzBuilder = new BenzBuilder();
staffV2.setBuilder(benzBuilder);
Car benzCar = staffV2.assembleCar();
System.out.println(benzCar);
}
}
在使用生成器模式重構(gòu)之后,你的汽車廠變得非常靈活然想。
調(diào)用者再也不需要了解造車的具體流程莺奔,只需調(diào)用對(duì)應(yīng)生成器的一個(gè)方法即可。
你的汽車廠業(yè)績(jī)蒸蒸日上又沾,你也超級(jí)開心弊仪。
三:再理解
- 生成器模式可以通過建立抽象生成器來約束生產(chǎn)過程,具體過程可以延遲到子類實(shí)現(xiàn)杖刷。
- 在需要生成新類時(shí)候,只需新建一個(gè)生成器類驳癌,無需在員工代碼中增加方法滑燃。符合對(duì)修改關(guān)閉,對(duì)增加開放原則颓鲜。
- 生成器模式將帶有邏輯的對(duì)象新建過程留在服務(wù)層表窘,對(duì)調(diào)用者透明。
- 客戶端無須知道復(fù)雜對(duì)象的內(nèi)部組成部分與裝配方式甜滨,只需要知道所需建造者的類型即可乐严,做到客戶端和產(chǎn)品創(chuàng)建過程的解耦。