《設計模式》中定義:
Builder模式的緣起: 假設創建游戲中的一個房屋House設施,該房屋的構建由幾部分組成,且各個部分富于變化。如果使用最直觀的設計方法,每一個房屋部分的變化,都將導致房屋構建的重新修正.....
動機(Motivation): 在軟件系統中,有時候面臨一個"復雜對象"的創建工作,其通常由各個部分的子對象用一定算法構成;由于需求的變化,這個復雜對象的各個部分經常面臨著劇烈的變化,但是將它們組合到一起的算法卻相對穩定。
如何應對種變化呢?如何提供一種"封裝機制"來隔離出"復雜對象的各個部分"的變化,從而保持系統中的"穩定構建算法"不隨需求的改變而改變?
意圖(Intent): 將一個復雜對象的構建與其表示相分離,使得同樣的構建過程可以創建不同的表示。
UML 表示如下:
|
Builder模式 |
這里的Builder是一個抽象類,不是接口,為了共用一些屬性和代碼,把稍微變化的部分讓子類來實現(顯然Builder 和 ConcreteOneProduct、ConcreteTwoProduct組成了個模板模式)。
先看建造者,一個抽象類,提供一些公用實現(Builder.java):
/** * 一個抽象來來替代接口 * * @author yongboy@gmail.com * @date 2010-10-26 * @version 1.0 */ public abstract class Builder { protected Product product = null;
public Builder() { product = new Product(); }
/** * 產生描述內容 */ public abstract void genDesc(String desc);
/** * 繪制三角形 */ public abstract void genTriangle(int len);
/** * 輸出最終產生的產品 * * @return */ public Product getProduct() { return product; } } |
一個實現,主要是繪制三角形:
public class ConcreteOneBuilder extends Builder {
@Override public void genDesc(String desc) { product.setDesc(desc); }
@Override public void genTriangle(int len) { StringBuilder sb = new StringBuilder(); for (int i = 1; i < len; i++) { for (int j = 0; j < i; j++) { sb.append(" *"); } sb.append("\n"); }
product.setContent(sb.toString()); } } |
第二個實現:
public class ConcreteTwoBuilder extends Builder {
@Override public void genDesc(String desc) { product.setDesc(desc); }
@Override public void genTriangle(int len) { StringBuilder sb = new StringBuilder();
for (int i = len; i > 0; i--) { int spaceNum = len - i;
for (int j = 0; j < spaceNum; j++) { sb.append(" "); } for (int j = 0; j < i; j++) { sb.append(" *"); }
sb.append("\n"); }
product.setContent(sb.toString()); } } |
產品定義:
/** * 一個有關三角的產品 * * @author yongboy@gmail.com * @date 2010-10-26 * @version 1.0 */ public class Product implements Serializable { private final static long serialVersionUID = 23536326475869L;
/** * 當前產品的描述 */ private String desc;
/** * 當前產品的詳細內容 */ private String content;
public Product() { }
public String getDesc() { return desc; }
public void setDesc(String desc) { this.desc = desc; }
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }
public String toString() { return desc + "\n" + content; } } |
導向器,封裝著產品生成的具體過程:
/** * 導向器,封裝產品生生成過程 * * @author yongboy@gmail.com * @date 2010-10-26 * @version 1.0 */ public class Director { private Builder builder; private int MAX = 25; private int MIN = 10;
public Director(Builder builder) { this.builder = builder; }
public void construct() { int len = getNext(); builder.genDesc("產品型號(" + getFormatNum(len) + "):"); builder.genTriangle(len); }
private int getNext() { return getRandomNum(MIN, MAX); }
private static String getFormatNum(int num) { return String.format("0.%d", num); } /** * 產生兩個數之間的隨機數 * * @param min * @param max * @return */ private int getRandomNum(double min, double max) { return (int) min + (int) (Math.random() * (max - min)); } } |
客戶端代碼調用方式:
public class Client { public static void main(String[] args) { Builder builder = new ConcreteOneBuilder();
Director director = new Director(builder);
director.construct();
Product product = builder.getProduct();
System.out.println(product);
System.out.println(); System.out.println("現在輸出第二個產品:\n"); System.out.println();
builder = new ConcreteTwoBuilder();
director = new Director(builder);
director.construct();
product = builder.getProduct();
System.out.println(product); } } |
輸入如下:
產品型號(0.23):
*
* *
* * *
* * * *
* * * * *
* * * * * *
* * * * * * *
* * * * * * * *
* * * * * * * * *
* * * * * * * * * *
* * * * * * * * * * *
* * * * * * * * * * * *
* * * * * * * * * * * * *
* * * * * * * * * * * * * *
* * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * *
現在輸出第二個產品:
產品型號(0.17):
* * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * *
* * * * * * * * * * * * * *
* * * * * * * * * * * * *
* * * * * * * * * * * *
* * * * * * * * * * *
* * * * * * * * * *
* * * * * * * * *
* * * * * * * *
* * * * * * *
* * * * * *
* * * * *
* * * *
* * *
* *
*
每一次運行可能生成變形長度都會變好,這個變好有導向器進行控制著。
源文件下載