《設計模式》上對建立者(Builder)模式的定義是將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。相信大部分初學者都看不懂這句話。
事實上,建立者模式是一步一步建立一個複雜的對象,它允許使用者可以只通過指定複雜物件的類型和內容就可以構建它們,使用者不知道內部的具體構建細節。Builder模式非常類似簡單原廠模式,細微的區別後面會提到。
因為一個複雜的對象,不但有很多大量組成部分,如汽車,有很多組件:車輪、方向盤、發動機還有各種小零件等等,組件很多,但遠不止這些。如何將這些組件裝配成一輛汽車,這個裝配過程也很複雜(需要很好的組裝技術),Builder模式就是為了將組件和組裝過程分開。這就是構建與表示的分離。有的書上也說建立者模式是為了將構建複雜物件的過程和它的組件解耦。注意:是解耦過程和組件。建立者模式本質是將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。
首先假設一個複雜物件是由多個組件組成的,Builder模式是把複雜物件的建立和組件的建立分別開來,分別用Builder介面表示組件建立,Director類來表示對象的建立。
首先,需要一個介面,它定義如何建立複雜物件的各個組件:
public interface Builder {
//建立組件A 比如建立汽車車輪
void buildPartA();
//建立組件B 比如建立汽車方向盤
void buildPartB();
//建立組件C 比如建立汽車發動機
void buildPartC();
//建立組件D 比如建立汽車車窗
void buildPartD();
//返回最後裝配成品結果 (返回最後裝配好的汽車)
//成品的組裝過程不在這裡進行,而是轉移到下面的Director類中進行.
//從而實現瞭解耦過程和組件
Product getResult();
}
Builder介面的具體實現ConcreteBuilder類完成介面Builder來構建或裝配產品的組件;定義並明確它所要建立的是什麼具體東西;提供一個可以重新擷取產品的介面:
package map_package;
public interface Map_interface {
public void create_weather();
public void create_house();
public void create_tree();
public void create_way();
}
public class ConcreteBuilder implements Builder {
Part partA, partB, partC;
public void buildPartA() {
//todo:這裡是具體如何構建partA的代碼
};
public void buildPartB() {
//todo:這裡是具體如何構建partB的代碼
};
public void buildPartC() {
//todo:這裡是具體如何構建partC的代碼
};
public void buildPartD() {
//todo:這裡是具體如何構建partD的代碼
};
public Product getResult() {
//返回最後裝配成品結果
};
}
最後,用Director構建最後的複雜物件,而在上面Builder介面中封裝的是如何建立一個個組件(複雜物件是由這些組件組成的),也就是說Director的內容是如何將組件最終組裝成成品:
public class Director {
private Builder builder;
public Director( Builder builder ) {
this.builder = builder;
}
// 將組件partA partB partC等最後組成複雜物件
// 這裡是將車輪 方向盤和發動機組裝成汽車的過程,
// 注意,其順序有一定的要求,比如這裡必須按A->D->C->B的順序
public void construct() {
builder.buildPartA();
builder.buildPartD();
builder.buildPartC();
builder.buildPartB();
}
}
複雜物件介面:產品Product:
public interface Product { …… }
複雜物件:
public class car implements Product { …… }
複雜物件的組件介面:
public interface Part { }
複雜物件的組件:
public class PartA implements Part { …… }
public class PartB implements Part { …… }
public class PartC implements Part { …… }
public class PartD implements Part { …… }
最後,讓我們看看如何調用Builder模式:
ConcreteBuilder builder = new ConcreteBuilder();
Director director = new Director(builder);
director.construct();
Product product = builder.getResult();
我們看到,跟簡單工廠最大的區別在於建立者模式並不關注對象的建立,而關注對象組件的建立,所以我們沒有在Director類中找到任何new關鍵字。
下面,我們就通過一個例子來講講建造者模式的JAVA代碼實現:
建立地圖介面:
package map_package;
public interface map_interface {
public void create_weather();
public void create_house();
public void create_tree();
public void create_way();
}
建立晴天地圖類:
package map_package;
public class map_sun implements map_interface {
public void create_weather() {
System.out.println("晴天");
}
public void create_house() {
System.out.println("房了上玻璃發亮");
}
public void create_tree() {
System.out.println("樹的顏色是淺綠色");
}
public void create_way() {
System.out.println("路面有些乾燥");
}
}
建立陰天地圖類:
package map_package;
public class map_cloudy implements map_interface{
public void create_weather() {
System.out.println("陰天");
}
public void create_house() {
System.out.println("房了上玻璃發暗");
}
public void create_tree() {
System.out.println("樹的顏色是深綠色");
}
public void create_way() {
System.out.println("路面有些潮濕");
}
}
好了,我們來看建造者模式如何封裝構造過程,提升我們的代碼品質,建立高畫質builder建造者類:
package map_build;
import map_package.map_interface;
public class map_build_adv {
private map_interface map_interface_ref;
public map_build_adv(map_interface map_interface_ref) {
super();
this.map_interface_ref = map_interface_ref;
}
public void create_map() {
System.out.println("建立一個高畫質的地圖");
// 建立的順序很重要 從天空往路面建立
map_interface_ref.create_weather();
map_interface_ref.create_house();
map_interface_ref.create_tree();
map_interface_ref.create_way();
}
}
建立低畫質builder建造類:
package map_build;
import map_package.map_interface;
public class map_build_low {
private map_interface map_interface_ref;
public map_build_low(map_interface map_interface_ref) {
super();
this.map_interface_ref = map_interface_ref;
}
public void create_map() {
System.out.println("建立一個低畫質的地圖");
// 建立的順序很重要 從天空往路面建立
map_interface_ref.create_weather();
map_interface_ref.create_house();
// map_interface_ref.create_tree();將建立樹的過程去掉
map_interface_ref.create_way();
}
}
工作做到位了,我們就可以使用它了:
package run_main;
import map_build.map_build_adv;
import map_build.map_build_low;
import map_package.map_cloudy;
import map_package.map_sun;
public class run_main {
public static void main(String[] args) {
map_cloudy map_cloudy = new map_cloudy();
map_build_adv map_build_adv = new map_build_adv(map_cloudy);
map_build_adv.create_map();
System.out.println();
map_sun map_sun = new map_sun();
map_build_low map_build_low = new map_build_low(map_sun);
map_build_low.create_map();
}
}
程式運行結果如下:
建立一個高畫質的地圖
陰天
房了上玻璃發暗
樹的顏色是深綠色
路面有些潮濕
建立一個低畫質的地圖
晴天
房了上玻璃發亮
路面有些乾燥
從程式中可以看到,建造者模式將不變的建立過程進行封裝,建立的過程與main分法進行分離,這樣內部的建立過程就和展示層的代碼進行分開,有利於建立過程功能上的修改。另外可以發現,代碼的設計和功能有些類似於facade面板模式,區別在於,建造者模式目的在於以相同的構建過程通過不同的建造者得到不 同的結果,而面板模式並不需要不同的建造者,也不希望得到不同的結果,只是簡單的將幾個介面合并成進階的一個介面,不影響原有的結果,目的是使調用變得更加容易。
本程式中有2個建造者:高畫者建造者和低畫質建造者,它們都封裝了建立地圖的過程,這個過程很固定,但通過不同的建造者類可以返回不樣式的地圖,建造者規定了對象建立的過程,比如高畫質的建造者的建立過程為:
map_interface_ref.create_weather();
map_interface_ref.create_house();
map_interface_ref.create_tree();
map_interface_ref.create_way();
必須得執行4個方法才可以建立一個高畫質的地圖,如果不使用建造者模式,直接調用map類的create_xxxx方法,如果create_xxxx方法 有幾十個,那麼很有可能就把其中的某些方法忘記調用而影響最終地圖的效果了,所以我們要使用建造者模式來規定地圖建立的過程,這就是一個“行為規則”。