在設計模式中,Factory Method 雖屬簡單,但應用極廣,EJB、RMI、COM、CORBA、Swing 等處處可見其身影,是最重要的模式之一。許多地方都能看到 xxxFactory 命名的類別。什麼是 Factory Method?為何使用?如何用 Java 實作?本文將一一解答。
Factory Method 屬於建立型模式,定義一個建立物件的介面,但讓子類別決定具體實例化哪個類別。當類別無法預知要建立哪種物件,或需要由子類別指定建立物件時,就需要 Factory Method 模式。
簡單說,Factory Method 可根據不同條件產生不同實例,這些實例通常屬於相同類型,擁有共同父類別。Factory Method 將建立實例的具體過程封裝起來,簡化了客戶端應用,改善程式擴展性,未來只需最小修改即可加入新的待建立類別。通常我們將 Factory Method 作為標準建立物件方法,當發現需要更多彈性時,再考慮轉向其他建立型模式。

圖 1:Factory Method 模式結構
術語定義:
由此可見平行對應關係:
Product <====> Creator
ConcreteProduct <====> ConcreteCreator
抽象產品對應抽象建立者,具體產品對應具體建立者。這樣做的好處?為何不直接用具體產品與具體建立者?實際上也可這樣做,但透過 Factory Method 模式,客戶端只需引用抽象的 Product 與 Creator,對具體 ConcreteProduct 與 ConcreteCreator 毫不關心,獲得額外好處:
優良物件導向設計鼓勵封裝與委派,Factory Method 正是典型範例:封裝透過抽象建立者 Creator 體現,委派透過抽象建立者將建立物件責任完全交給具體建立者 ConcreteCreator 體現。
先說明:用 Factory Method 建立物件不一定讓程式碼更短,往往更長,使用更多類別,真正目的在於靈活、彈性地建立不確定物件。程式碼重用性提高,客戶端應用簡化,客戶程式碼大幅減少、更具可讀性。
採用 Bruce Eckel 描述 OO 思想的經典範例 Shape。完全依照圖 1 結構撰寫示範程式碼,功能為建立不同 Shape 實例,每個實例完成 draw 與 erase 兩個操作,具體建立過程委派給 ShapeFactory。
abstract class Shape {
// 勾畫 shape
public abstract void draw();
// 擦除 shape
public abstract void erase();
public String name;
public Shape(String aName) {
name = aName;
}
}
// 圓形子類別
class Circle extends Shape {
public void draw() {
System.out.println("It will draw a circle.");
}
public void erase() {
System.out.println("It will erase a circle.");
}
// 建構函式
public Circle(String aName) {
super(aName);
}
}
// 方形子類別
class Square extends Shape {
public void draw() {
System.out.println("It will draw a square.");
}
public void erase() {
System.out.println("It will erase a square.");
}
// 建構函式
public Square(String aName) {
super(aName);
}
}
abstract class ShapeFactory {
protected abstract Shape factoryMethod(String aName);
// 在 anOperation 中定義 Shape 一系列行為
public void anOperation(String aName) {
Shape s = factoryMethod(aName);
System.out.println("The current shape is: " + s.name);
s.draw();
s.erase();
}
}
// 定義回傳 Circle 實例的 CircleFactory
class CircleFactory extends ShapeFactory {
// 覆寫 factoryMethod 方法,回傳 Circle 物件
protected Shape factoryMethod(String aName) {
return new Circle(aName + " (created by CircleFactory)");
}
}
// 定義回傳 Square 實例的 SquareFactory
class SquareFactory extends ShapeFactory {
// 覆寫 factoryMethod 方法,回傳 Square 物件
protected Shape factoryMethod(String aName) {
return new Square(aName + " (created by SquareFactory)");
}
}
class Main {
public static void main(String[] args) {
ShapeFactory sf1 = new SquareFactory();
ShapeFactory sf2 = new CircleFactory();
sf1.anOperation("Shape one");
sf2.anOperation("Shape two");
}
}
執行結果:
The current shape is: Shape one (created by SquareFactory)
It will draw a square.
It will erase a square.
The current shape is: Shape two (created by CircleFactory)
It will draw a circle.
It will erase a circle.
依靠指定參數作為標記建立對應實例,常見做法。如 JFC 中 BorderFactory 即是佳例。以下範例用字串作為標記判斷,若參數型別不同,可用多載函式解決,定義一系列參數與方法本體不同的同名函式,java.util.Calendar.getInstance() 又是極好範例。
參數化建立方式克服 Factory Method 一個顯著缺陷:具體產品較多時,不得不建立一系列對應具體建構器。但在客戶端必須指定參數決定建立哪個類別。
class NoThisShape extends Exception {
public NoThisShape(String aName) {
super(aName);
}
}
ShapeFactory 自己變成具體建立者,直接用參數化方法實作 factoryMethod 回傳多種物件。(程式碼省略,請參考原文完整實作)