關(guān)于建造者模式(或者又叫構(gòu)造者模式),我在網(wǎng)上看了很多文章。其中不乏很多人直接把建造者模式等同于builder構(gòu)造器。我想說這是兩種完全不同的層次和方向,一種是從系統(tǒng)設(shè)計層面考慮的設(shè)計模式,用于使整個系統(tǒng)構(gòu)件間解耦更加明顯,更易于擴展;另一種則是為了讓構(gòu)造器能適應(yīng)多個參數(shù)而出現(xiàn)的構(gòu)造函數(shù)變體。
這里不再對網(wǎng)上漫天的Builder構(gòu)造器進行解釋,只針對建造者模式進行討論。
建造者模式:將類的使用者與類的建造方式解耦,將類的使用者與類的建造者解耦。
當(dāng)然,上面這句話也是我個人對建造者模式的理解。建造者模式就是實現(xiàn)了三點之間的解耦:類的使用者、類的建造者、類的建造方式。
我們先來看一下建造者模式的示意類圖:

- Builder, 抽象建造者,定義了建造一個類(Product)需要實現(xiàn)的方法;
- ConcreteBuilder,具體建造者,實現(xiàn)了抽象建造者定義的方法;
- Product,產(chǎn)品,其實就是建造者需要建造的東西;
- Director,其實它才是真正的類的建造者,上面所說的Builder,其實只是定義了類的建造方式,但是真正和類的使用者交互的是Director,它作為具體類的提供者,完成了建造的步驟。
OK, 我們現(xiàn)在來從上面說的兩個解耦進行后續(xù)的解釋。
將類的使用者與類的建造方式解耦
平常我們想進行一個類的構(gòu)造會怎么寫?一般來說我們先new一個實例出來,然后對各個變量進行set,最后做我們想做的事情。這里大家也可以換成直接在構(gòu)造函數(shù)里面?zhèn)魅雲(yún)?shù),大致意思是一樣的。
Foo foo = new Foo();
foo.setA(123);
foo.setB("abc");
foo.setC(new bar());
foo.doSomething();
但是這種做法,實際上將類的初始化和各項配置工作都移交給了類的使用者來做,大大增強了類的使用者和類的建造方式之間的耦合關(guān)系,類的使用者必須完全了解如何建造這個類才可以,否則便等不到一個功能完好的實例,更不要說正確地使用了。
OK,那如果說我現(xiàn)在有這樣一個產(chǎn)品類,會被別人使用到。我們先假設(shè)產(chǎn)品類是這個樣子:
public class Product {
private String name;
private BigDecimal price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
}
而我們存在多種建造這個產(chǎn)品的方式,但首先我們需要定義一個接口,用以確定產(chǎn)品的建造者需要做哪些工作:
public interface IProductBuilder {
public void giveName();
public void givePrice();
public Product build();
}
而后我們有了兩個建造商品的類,一個生產(chǎn)高質(zhì)量商品,另一個生產(chǎn)普通商品:
public class HighQualityProductBuilder implements IProductBuilder{
private Product product = new Product();;
@Override
public void giveName() {
product.setName("A High Quality Product comes out!");
}
@Override
public void givePrice() {
product.setPrice(new BigDecimal("299.99"));
}
@Override
public Product build() {
giveName();
givePrice();
refine();
return product;
}
private void refine(){
System.out.println("Don't know how, but the product get refined!");
}
}
public class NormalProductBuilder implements IProductBuilder {
private Product product = new Product();;
@Override
public void giveName() {
product.setName("Product with normal quality......");
}
@Override
public void givePrice() {
product.setPrice(new BigDecimal("1.59"));
}
@Override
public Product build() {
giveName();
givePrice();
return product;
}
}
這個時候我們能夠看到,在Builder類中,我已經(jīng)將值賦給了product的域,并且在build()方法中實現(xiàn)了產(chǎn)品的裝配和建造過程。后面會提到,這其實是建造者模式的一大問題所在。
而后,我們在main方法中模擬使用該類,這樣其實就能夠?qū)崿F(xiàn)了將類的使用者與類的建造方式解耦這一目的:
public static void main(String[] args){
IProductBuilder builder = new HighQualityProductBuilder();
Product product = builder.build();
}
但是到此為止,我們做得還不夠。大家可以看到,在類圖中展示的Builder、ConcreteBuilder、和Product都已經(jīng)出現(xiàn)了,但是Director還遲遲不肯露面。下面我們就來說一下Director在整個建造者模式中的作用。
將類的使用者與類的建造者解耦
為了實現(xiàn)這一解耦目標(biāo),我們引入了Director這個參與者。它主要做的工作,就是封裝了Builder,然后直接和外界類的使用者進行交互:
public class Director {
private IProductBuilder builder;
public Director(IProductBuilder builder){
this.builder = builder;
}
public Product construct(){
return builder.build();
}
}
而后我們在main方法中使用director來替代原來直接寫builder的方式:
public static void main(String[] args){
Director director = new Director(new NormalProductBuilder());
Product product = director.construct();
}
看到這里,很多人可能會罵我根本就不懂。這不還是寫死了NormalProductBuilder了么?不照樣是緊耦合了么?有什么意義?其實如果大家寫過一點Spring就能頓悟這里面的妙處,只要我在Director構(gòu)造時使用注入,并將其實現(xiàn)為一個Bean,就可以很輕松地將其進行解耦。由一個Director來作為選擇和管理Builder的入口,而通過注入的方式將使用者與后方隔離開來,這才是建造者模式真正強大的地方。
一大問題
另外我上面也說了,建造者模式的一大問題,就是它把類的建造過程寫死了,綁死了,寫成了一套流程化的代碼邏輯,不易更改。
所以說,建造者模式適用于不易變更的且構(gòu)建復(fù)雜的類的實例化場景,而對那些生成過程經(jīng)常改變的類,則不建議使用該模式,因為得到的收益可能遠比不上修改的成本。