將複雜物件的建構過程與其表示分離,使相同建構流程能產生不同表示。適用於:建構步驟固定、但各步驟具體實現多變、物件屬性眾多或有順序依賴。
// 產品
class Pizza {
private String dough, sauce, topping;
// setter/getter 省略
}
// 抽象建造者
abstract class PizzaBuilder {
protected Pizza pizza = new Pizza();
public Pizza getPizza() { return pizza; }
public void createNewPizza() { pizza = new Pizza(); }
public abstract void buildDough();
public abstract void buildSauce();
public abstract void buildTopping();
}
// 具體建造者:夏威夷披薩
class HawaiianPizzaBuilder extends PizzaBuilder {
public void buildDough() { pizza.setDough("cross"); }
public void buildSauce() { pizza.setSauce("mild"); }
public void buildTopping() { pizza.setTopping("ham+pineapple"); }
}
// 具體建造者:義式辣味披薩
class SpicyPizzaBuilder extends PizzaBuilder {
public void buildDough() { pizza.setDough("pan baked"); }
public void buildSauce() { pizza.setSauce("hot"); }
public void buildTopping() { pizza.setTopping("pepperoni+salami"); }
}
// 指揮者
class Waiter {
private PizzaBuilder builder;
public void setBuilder(PizzaBuilder b) { builder = b; }
public Pizza getPizza() { return builder.getPizza(); }
public void construct() {
builder.createNewPizza();
builder.buildDough();
builder.buildSauce();
builder.buildTopping();
}
}
// 客戶端
public class BuilderDemo {
public static void main(String[] args) {
Waiter waiter = new Waiter();
waiter.setBuilder(new HawaiianPizzaBuilder());
waiter.construct();
Pizza pizza = waiter.getPizza(); // 獲得夏威夷披薩
}
}
public class User {
private final String name;
private final int age;
private final String email;
private final String phone;
private User(Builder builder) {
this.name = builder.name;
this.age = builder.age;
this.email = builder.email;
this.phone = builder.phone;
}
public static class Builder {
private String name, email, phone;
private int age;
public Builder name(String v) { name = v; return this; }
public Builder age(int v) { age = v; return this; }
public Builder email(String v) { email = v; return this; }
public Builder phone(String v) { phone = v; return this; }
public User build() { return new User(this); }
}
}
// 使用
User u = new User.Builder().name("Alice").age(25).email("a@b.com").build();
透過複製現有實例建立新實例,而非 new。適用於:建立成本高(大量資料載入、複雜計算)、或需建立相似但非完全相同的物件。
clone() 介面clone() 獲得新實例
// 淺複製:預設 Object.clone() 行為
class Shallow implements Cloneable {
int age;
Person person; // 參考型別
public Object clone() throws CloneNotSupportedException {
return super.clone(); // 參考型別共享同一個 Person 物件
}
}
// 深複製:手動複製參考型別
class Deep implements Cloneable {
int age;
Person person;
public Object clone() throws CloneNotSupportedException {
Deep d = (Deep) super.clone();
d.person = new Person(this.person.getName()); // 新建 Person
return d;
}
}
// 或實作 Serializable 透過序列化深複製
Shallow s1 = new Shallow(); s1.age=20; s1.person=new Person("Eric");
Shallow s2 = (Shallow) s1.clone();
s1.age = 30; s1.person.setName("Frank");
System.out.println(s2.person.getName()); // Frank → 共享同一 Person
Deep d1 = new Deep(); d1.age=20; d1.person=new Person("Eric");
Deep d2 = (Deep) d1.clone();
d1.age = 30; d1.person.setName("Frank");
System.out.println(d2.person.getName()); // Eric → 獨立 Person
class PrototypeManager {
private Map<String, Prototype> prototypes = new HashMap<>();
public void register(String key, Prototype p) { prototypes.put(key, p); }
public Prototype get(String key) {
return prototypes.get(key).clone(); // 取得複製品
}
}
new(避免重複初始化邏輯)Cloneable 並覆寫 clone() 拋出 CloneNotSupportedException 為受檢異常,需處理| 維度 | 建造者模式 | 原型模式 |
|---|---|---|
| 核心手段 | 分步驟組裝 | 複製現有實例 |
| 適用物件 | 建構邏輯複雜、參數多、不可變 | 建立成本高、或需大量相似實例 |
| 擴充方式 | 新增 ConcreteBuilder | 新增/註冊 Prototype 實例 |
| 狀態管理 | Director 控制流程 | 原型實例保持狀態快照 |
| 典型案例 | StringBuilder、DocumentBuilder、HttpClient.Builder | 遊戲實體複製、報表模板複製、原型管理器 |
建造者模式解決「如何優雅建構複雜物件」,原型模式解決「如何低成本複製現有物件」。實務中常搭配使用:原型作為建造者的預設模板,建造者在複製基礎上進行客製化修改。掌握兩者精髓,能靈活應對各類物件創建挑戰。