首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 中應用 Factory Method 設計模式完整指南

     編輯:Java 相關知識

基本概念

在設計模式中,Factory Method 雖屬簡單,但應用極廣,EJB、RMI、COM、CORBA、Swing 等處處可見其身影,是最重要的模式之一。許多地方都能看到 xxxFactory 命名的類別。什麼是 Factory Method?為何使用?如何用 Java 實作?本文將一一解答。

Factory Method 屬於建立型模式,定義一個建立物件的介面,但讓子類別決定具體實例化哪個類別。當類別無法預知要建立哪種物件,或需要由子類別指定建立物件時,就需要 Factory Method 模式。

簡單說,Factory Method 可根據不同條件產生不同實例,這些實例通常屬於相同類型,擁有共同父類別。Factory Method 將建立實例的具體過程封裝起來,簡化了客戶端應用,改善程式擴展性,未來只需最小修改即可加入新的待建立類別。通常我們將 Factory Method 作為標準建立物件方法,當發現需要更多彈性時,再考慮轉向其他建立型模式。

簡單分析

Factory Method 模式結構圖

圖 1:Factory Method 模式結構

術語定義:

  1. Product:需建立產品的抽象類別。
  2. ConcreteProduct:Product 子類別,一系列具體產品。
  3. Creator:抽象建立者介面,宣告回傳 Product 型別物件的 Factory Method。
  4. ConcreteCreator:具體建立者,覆寫 Creator 中的 Factory Method,回傳 ConcreteProduct 型別實例。

由此可見平行對應關係:

Product <====> Creator
ConcreteProduct <====> ConcreteCreator

抽象產品對應抽象建立者,具體產品對應具體建立者。這樣做的好處?為何不直接用具體產品與具體建立者?實際上也可這樣做,但透過 Factory Method 模式,客戶端只需引用抽象的 Product 與 Creator,對具體 ConcreteProduct 與 ConcreteCreator 毫不關心,獲得額外好處:

  • 客戶端統一從抽象建立者獲取實例,Creator 將客戶端與產品建立過程分離,客戶端無需操心回傳哪個具體產品,也無需關心產品如何建立。
  • ConcreteProduct 隱藏在 Product 後面,繼承 Product 所有屬性並實作抽象方法。根據 Java 物件造型原則,ConcreteCreator 產生的 ConcreteProduct 可自動上溯造型為 Product。不同實質內容的 ConcreteProduct 在形式上統一為 Product,透過 Creator 提供給客戶端存取。
  • 新增 ConcreteCreator 時,Creator 提供的介面不變,客戶端程式零修改,避免牽一髮動全身,體現良好封裝性。直接用兩個具體類別無法做到這點。

優良物件導向設計鼓勵封裝與委派,Factory Method 正是典型範例:封裝透過抽象建立者 Creator 體現,委派透過抽象建立者將建立物件責任完全交給具體建立者 ConcreteCreator 體現。

具體實作

先說明:用 Factory Method 建立物件不一定讓程式碼更短,往往更長,使用更多類別,真正目的在於靈活、彈性地建立不確定物件。程式碼重用性提高,客戶端應用簡化,客戶程式碼大幅減少、更具可讀性。

標準實作

採用 Bruce Eckel 描述 OO 思想的經典範例 Shape。完全依照圖 1 結構撰寫示範程式碼,功能為建立不同 Shape 實例,每個實例完成 draw 與 erase 兩個操作,具體建立過程委派給 ShapeFactory。

1.a 定義抽象類別 Shape

abstract class Shape {
    // 勾畫 shape
    public abstract void draw();
    // 擦除 shape
    public abstract void erase();

    public String name;
    public Shape(String aName) {
        name = aName;
    }
}

1.b 定義 Shape 兩個子類別:Circle、Square

// 圓形子類別
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);
    }
}

1.c 定義抽象建立者

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();
    }
}

1.d 定義對應 Circle、Square 的具體建立者

// 定義回傳 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)");
    }
}

1.e 測試類別:客戶端程式多麼簡潔

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.

參數化 Factory Method

依靠指定參數作為標記建立對應實例,常見做法。如 JFC 中 BorderFactory 即是佳例。以下範例用字串作為標記判斷,若參數型別不同,可用多載函式解決,定義一系列參數與方法本體不同的同名函式,java.util.Calendar.getInstance() 又是極好範例。

參數化建立方式克服 Factory Method 一個顯著缺陷:具體產品較多時,不得不建立一系列對應具體建構器。但在客戶端必須指定參數決定建立哪個類別。

2.a 自定義例外

class NoThisShape extends Exception {
    public NoThisShape(String aName) {
        super(aName);
    }
}

2.b 移除 ShapeFactory 兩個子類別,改由 ShapeFactory 直接負責實例建立

ShapeFactory 自己變成具體建立者,直接用參數化方法實作 factoryMethod 回傳多種物件。(程式碼省略,請參考原文完整實作)

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved