開篇廢話
周末因懶的原因,停更了兩天。今天主要研究建造者模式。暢游各大博客網(wǎng)站,推薦一篇關(guān)于建造者模式的博客http://blog.csdn.net/self_study/article/details/51707029,簡單明了,條例清晰。如果看不懂我寫的,可以嘗試去看看別人的。。。
什么是建造者模式
講建造者模式,這里要提取幾個關(guān)鍵詞:
- 復(fù)雜對象: 這里簡單的體現(xiàn)可以理解為,這里對象的成員數(shù)量很多,所以它很復(fù)雜。
- 構(gòu)建過程: 對成員對象進(jìn)行賦值,業(yè)務(wù)邏輯生成等一系列使之成為一個成熟的對象。
- 表示: 返回這個對象。
- 構(gòu)建過程與表示分離: 一般來說,對成員對象的賦值通常在構(gòu)造函數(shù)中,這種方式除了不夠靈活之外,還有就是如果成員對象過多,因?yàn)闃?gòu)造函數(shù)就會顯得十分臃腫。所以,我們可以將構(gòu)建過程(簡單的理解為賦值,這里不嚴(yán)謹(jǐn),只是為了幫助理解概念)封裝成獨(dú)立的方法,然后再返回整個需要的實(shí)例對象。
那么建造者模式:就是為了解決生成復(fù)雜對象的問題,主要通過將構(gòu)建過程與表示分離的方法。
如何實(shí)現(xiàn)建造者模式
理解了建造者模式后,我們來實(shí)現(xiàn)它,就相對簡單了。
首先盜圖一張:

可以看到,這里呈現(xiàn)了四個類,有時候?qū)嶋H開發(fā)中會將這個模型進(jìn)行簡化,我們這里先學(xué)習(xí)理論,因?yàn)閷?shí)際操作時,靈活變動太多了,不適合講解。但是萬變不離其宗,核心思想就是這些。
- Product,都稱這個為產(chǎn)品類,說白了就是我需要生成的類。
- Builder類,這是一個抽象類或者接口,用來規(guī)定構(gòu)建對象的所需方法。
- ConcreteBuilder 對builder的抽象方法的實(shí)現(xiàn)類,通常對其優(yōu)化為product的內(nèi)部類,從而省掉builder的定義。常見的有 AlertDialog.Builder 。
- Director 調(diào)用concrete的方法的類。一般來說,使用的建造者模式時,這個也給省了。
我們先來看一個完整版的。以造汽車為例,現(xiàn)在我們需要一輛,有四個輪子,有四個座椅,有防風(fēng)玻璃,有方向盤,發(fā)動機(jī)等等(這里只列舉這幾樣)。
- 首先我們先看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();
}
這里定義了五個造小汽車零件的方法,和一個生成對象方法,因?yàn)槟悴荒苤辉爝@些零件,還需要把這些零件給組裝起來啊。
- 緊接著我們需要ConcreteBuilder類來實(shí)現(xiàn)這些方法啊,畢竟我們是實(shí)干家,不能光說不做!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ī)已經(jīng)做好了");
}
@Override
public Car build() {
return car;
}
}
這里實(shí)現(xiàn)了builder接口,沒什么好說的,值得注意的就是,在CarBuilder類中,維護(hù)了一個Car對象,實(shí)現(xiàn)的所有方法中,去調(diào)用Car的set方法,最后build()方法中去返回這個對象。
此時此刻,我們已經(jīng)能通過CarBuilder類去建造我們所需要的Car對象了。但是畢竟造汽車的工序是及其復(fù)雜了,因?yàn)槲覀冞@里只簡單的列舉了五項(xiàng),可能感覺還是比較輕松的,但是,如果有上百道工序的話,難道我們需要每次去造小車的時候都去調(diào)用這上百個方法嗎。顯然,這里構(gòu)建的過程,我們希望是不透明的,那么我們可以將這個步驟封裝起來,于是就有了Director類的出現(xiàn)。
- 我們希望有一個技術(shù)總工來調(diào)度負(fù)責(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è)計(jì)造車的工序,安排下去后,工人們按命令執(zhí)行就行了。而對于買家來說,我并看不到你是怎么造的,反正我只要結(jié)果就行了。
但是這樣往往會很麻煩,我們通常會對其進(jìn)行精簡優(yōu)化。
看上面對建造者定義了四個功能模塊,在實(shí)現(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,其實(shí)對應(yīng)的是建造者模式中的concreteBuilder,Computer對應(yīng)的就是我們product。去掉了Builder抽象類,因?yàn)橹苯訉?shí)現(xiàn)了。同時還缺少了Directer,因?yàn)檫@里的設(shè)計(jì)是暴露構(gòu)建細(xì)節(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)用,也是我們常見的建造者模式。我們可以對每個成員對象進(jìn)行控制,從而得到我們想要的product。當(dāng)然,我們看回Computer類的實(shí)現(xiàn),發(fā)現(xiàn),每個成員對象都是有個默認(rèn)值的。也就是說,你完全可以通過new Computer.Builder().create()來獲取默認(rèn)的實(shí)例對象,這基本就是Directer的功能。所以精簡下來,發(fā)現(xiàn)建造者模式還是挺實(shí)用的。
然而在大型的系統(tǒng)中,精簡版的建造者模式,并不是很適用,因?yàn)樗尼槍π蕴珡?qiáng),往往呈現(xiàn)出來的是同一種產(chǎn)品,拓展性并不是很好,而我們使用原始版的話,可以對Directer進(jìn)行拓展,可以更加多元靈活。