建造者模式,有4個角色:
- 導演角色(Director)
- 抽象創(chuàng)建者角色 (Builder)
- 具體創(chuàng)建者角色(ConcreteBuilder)
-
需要創(chuàng)建的產(chǎn)品(Product)
類圖如下:
創(chuàng)建者模式類圖.png
舉例代碼如下:
假設客戶(Client)現(xiàn)在要造一個房子(House),客戶不會做,客戶就找到包工頭(Director),把需求告訴他,但是做房子很麻煩,包工頭也干不完,于是請來了施工隊(ConcreteBuilder),在做房子這個行業(yè)里,施工隊都是按一定的標準做的(Builder).
首先我們要先有一個房子的實體類
package com.byedbl.builder;
/*
* A house as a concrete product we got finally
*/
public class House {
int roomNumber;
int doorNumber;
public House() {
roomNumber = 0;
doorNumber = 0;
}
public int getRoomNumber() {
return roomNumber;
}
public int getDoorNumber() {
return doorNumber;
}
}
接著要有個包工頭(Director),包工頭知道怎么指揮施工隊(Builder)
package com.byedbl.builder;
/*
* This class is a Director
*/
public class HouseDirector {
public void CreateHouse(HouseBuilder concreteBuilder) {
concreteBuilder.BuildRoom(1);
concreteBuilder.BuildRoom(2);
concreteBuilder.BuildDoor(1, 2);
//return builder.getHouse();
}
}
接著要請個有一定標準的施工隊.沒標準建出來的就比較恐怖了.
先立標準
package com.byedbl.builder;
/*
* An abstract Builder
*/
public abstract class HouseBuilder {
public abstract void BuildRoom(int roomNo);
public abstract void BuildDoor(int room1, int room2);
public abstract House getHouse();
}
在找個會這個標準的施工隊
package com.byedbl.builder;
public class ConcreteHouseBuilderA extends HouseBuilder{
private House house;
public ConcreteHouseBuilderA() {
house = new House();
}
public void BuildRoom(int roomNo) {
//you can create a new Room added to a House
house.roomNumber++;
}
public void BuildDoor(int room1, int room2) {
// you can create a new door assotiated with 2 room
// and added this door into a house
house.doorNumber++;
}
public House getHouse() {
return house;
}
}
整個流程下來,客戶創(chuàng)建房子是這樣子的.
package com.byedbl.builder;
/**
* 建造者角色:
* <ul>
* <li>導演角色(Director)
* <li>創(chuàng)建者角色
* <li>具體創(chuàng)建者角色
* <li>產(chǎn)品,產(chǎn)品可能也有一個抽象類
* </ul>
* <pre>
* 建造者模式適合場景:
* 1. 產(chǎn)品內部有復雜的結構
* 2. 產(chǎn)品對象的屬性相互依賴,比如郵件,有前后依賴關系,缺一不可,這種可以在 Director 角色里規(guī)定必要的流程
* ConcreteBuilder 負責具體的創(chuàng)建工作
* 3. 換一個 ConcreteBuilder 就換了一種款式的產(chǎn)品.很方便
* </pre>
* <p> 與工廠模式的區(qū)別:
* 兩者都是創(chuàng)建產(chǎn)品,建造者用意是得到宏觀的層面得到一個最終的產(chǎn)品,而工廠模式可能是產(chǎn)品的一個零件
**/
public class TestClient {
public TestClient() {
}
public static void main(String[] args) {
House myHouse ;
ConcreteHouseBuilderA myHouseBuilder = new ConcreteHouseBuilderA();
HouseDirector myHouseDirector = new HouseDirector();
myHouseDirector.CreateHouse(myHouseBuilder);
myHouse = myHouseBuilder.getHouse();
System.out.println("My house has room :" + myHouse.getRoomNumber());
System.out.println("My house has door :" + myHouse.getDoorNumber());
}
}
雖然客戶不會創(chuàng)建房子,但是聽說包工頭(Director)專門干這事,于是我們客戶就找個包工頭new HouseDirector()
,包工頭就向外喊,誰誰誰會造這樣子的房子(立了個標準,Builder),這時ConcreteHouseBuilderA
就說我會,然后房子就這樣造出來了,其實包工頭就是動動嘴而已,真正干活的還是ConcreteHouseBuilderA
構建者有什么用呢?
- 在創(chuàng)建比較復雜對象的時候很有用,
- 換一種
ConcreteHouseBuilder
就是不同的產(chǎn)品,但是因為有Director的標準流程在那,產(chǎn)品也不會走型. - 客戶端無需知道具體的創(chuàng)建過程.
- 通過建造者模式構造一個類,達到一個鏈式創(chuàng)建的效果
public class NutritionFacts {
//定義一堆屬性
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
//定義一個靜態(tài)類Builder
public static class Builder{
// 定義一堆和上面一樣的屬性,并加上默認值
private final int servingSize;
private final int servings;
private int calories=0;
private int fat=0;
private int carbohydrate=0;
private int sodium=0;
public Builder( int servingSize, int servings){
this.servingSize=servingSize;
this.servings=servings;
}
public Builder calories( int val){
calories=val;
//返回Builder
return this;
}
public Builder fat( int val){
fat=val;
return this;
}
public Builder carbohydrate( int val){
carbohydrate=val;
return this;
}
public Builder sodium( int val){
sodium=val;
return this;
}
//提供build方法,調用外圍類的構造器創(chuàng)建最終的類
public NutritionFacts build(){
return new NutritionFacts( this);
}
}
private NutritionFacts(Builder builder){
servingSize=builder.servingSize;
servings=builder.servings;
calories=builder.calories;
fat=builder.fat;
sodium=builder.sodium;
carbohydrate=builder.carbohydrate;
}
public static void main(String[] args) {
NutritionFacts nu= new NutritionFacts.Builder(240, 8).calories(100).sodium(35).build();
System.out.println(nu.calories);
System.out.println(nu.servingSize);
}
}