iT邦幫忙

2025 iThome 鐵人賽

DAY 11
0
自我挑戰組

一條龍的軟體開發到維護,從校園工讀到職場工程師系列 第 11

Day11-Code design我遇到比較常見的patten/工廠模式 (Factory Pattern)

  • 分享至 

  • xImage
  •  

首先講一下工廠模式 (Factory Pattern)

算是最常用的一個模式,在很多coding的時候你會不經意地使用他

定義一個用於建立物件的介面,但讓子類別決定要實例化哪一個類別。
工廠方法讓一個類別的實例化延遲到其子類別。

在沒有使用工廠模式的情況下,我們的程式碼(客戶端)通常需要自己使用 new 關鍵字來建立具體的物件。
例如:

// 不好的作法:客戶端直接依賴具體的類別
Product myProduct = new ConcreteProductA();

這樣做的壞處是,客戶端程式碼與 ConcreteProductA 這個具體的類別緊密耦合 (Tightly Coupled)。如果未來我們需要將 ConcreteProductA 換成 ConcreteProductB,就必須修改所有建立物件的客戶端程式碼

工廠模式透過引入一個「工廠」來解決這個問題。客戶端不再直接建立物件,而是向工廠請求一個物件。工廠內部會根據收到的指令或條件,決定到底要建立哪一個具體的物件並回傳。如此一來,客戶端只需要認識這個工廠和它所生產的產品介面,而不需要知道背後實際的產品類別是什麼。

程式碼範例 (以 Java 為例)

// Client.java (應用程式主邏輯)
String fileType = "pdf";
Document myDoc;

if (fileType.equals("pdf")) {
    myDoc = new PdfDocument(); // 直接依賴 PdfDocument
} else if (fileType.equals("word")) {
    myDoc = new WordDocument(); // 直接依賴 WordDocument
} else {
    throw new Exception("不支援的檔案類型");
}

myDoc.open();

這段程式碼有幾個嚴重的問題:

緊密耦合 (Tight Coupling): Client 程式碼直接認識並依賴 PdfDocument 和 WordDocument 這兩個具體的類別。Client 和它們被緊緊地綁在一起。

違反開放封閉原則 (Open/Closed Principle): 這個原則主張「軟體實體(類別、模組、函式等)應該對擴充開放,對修改封閉」。在上面的例子中,如果我們未來需要新增一種新的文件類型,例如 TextDocument,我們就必須回來修改 Client 程式碼,在 if-else 結構中增加一個新的分支。這就是「對修改不封閉」。每次新增功能都要修改既有程式碼,會讓系統變得脆弱且難以維護。

建立邏輯散落各處: 如果應用程式中有很多地方需要建立文件,那麼這段 if-else 的判斷邏輯就必須在每個地方都複製一遍。

我們來動手重構上面的程式碼。

第 1 步:定義產品介面與具體產品 (維持不變)
這部分和原本一樣,我們需要一個抽象的產品和多個具體的產品。

// 產品介面
interface Document {
    void open();
    void close();
}

// 具體產品 A
class PdfDocument implements Document {
    public void open() { System.out.println("正在開啟 PDF 文件..."); }
    public void close() { System.out.println("正在關閉 PDF 文件..."); }
}

// 具體產品 B
class WordDocument implements Document {
    public void open() { System.out.println("正在開啟 Word 文件..."); }
    public void close() { System.out.println("正在關閉 Word 文件..."); }
}

第 2 步:建立工廠
現在,我們建立一個工廠類別 DocumentFactory,把那段醜陋的 if-else 搬進去。

// 這是一個簡單工廠 (Simple Factory) 的實作
class DocumentFactory {
    // 工廠的核心方法,負責建立物件
    public static Document createDocument(String type) throws Exception {
        if (type.equalsIgnoreCase("pdf")) {
            return new PdfDocument();
        } else if (type.equalsIgnoreCase("word")) {
            return new WordDocument();
        } else {
            throw new Exception("不支援的檔案類型: " + type);
        }
    }
}

第 3 步:客戶端使用工廠
現在,客戶端程式碼變得非常乾淨。它不再需要知道 PdfDocument 或 WordDocument 的存在,也不再包含任何 if-else 判斷。

// Client.java (改善後)
public class Client {
    public static void main(String[] args) {
        try {
            // 客戶端不再直接 new 具體產品
            // 而是向工廠請求一個產品
            String fileType = "word";
            Document myDoc = DocumentFactory.createDocument(fileType); // 重點!

            myDoc.open();
            myDoc.close();

        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
    }
}

透過上面的改善過程,我們可以總結出工廠模式的定義:

工廠模式 (Factory Pattern) 它提供了一種創建物件的最佳方式,讓我們可以將物件的創建邏輯封裝在一個單獨的類別(工廠)中,而不是將其散佈在應用程式的各個角落。客戶端在使用物件時,只需向工廠請求,而無需知道物件是如何被創建的,也無需關心具體的實現類別是什麼。

這次重構帶來的好處:
解耦 (Decoupling): Client 現在只依賴 Document 介面和 DocumentFactory 工廠,完全不依賴任何具體的 PdfDocument 或 WordDocument 類別。耦合度大大降低。

單一職責原則 (Single Responsibility Principle): 物件的創建和使用被分開了。工廠專門負責創建,客戶端專門負責使用,各司其職。

好的,這是一個非常精準的總結。我們將你的觀點組織成一段完整的敘述,來說明為何在大型專案中,工廠模式是不可或缺的。

總結:為何在大型專案中採納工廠模式?
在大型專案中,採納工廠模式並非只是換一種寫法,而是一種關鍵的架構策略,其核心價值在於將「物件的建立過程」與「物件的使用過程」徹底分離。這種分離帶來了三大顯著優勢:

首先,它極大地減少了因物件建立邏輯複雜而導致的程式碼錯誤。在一個龐大的系統中,建立一個物件可能需要複雜的初始化步驟或依賴外部設定。如果將這些複雜的 new 操作散落在程式碼的各個角落,一旦建立邏輯需要修改,就必須找出所有地方逐一修正,這不僅耗時,也極易出錯。工廠模式將這些邏輯集中在單一的「工廠」中,任何修改或錯誤修正都只需要在一個地方完成,確保了物件建立的一致性與正確性。

其次,它顯著增加了系統的延展性(擴充性)。這正是你提到的「不會把每一個 function 包在一起」的體現,也就是遵循了「單一職責」與「開放封閉」原則。當系統需要支援一個新的產品(例如一種新的文件格式或資料庫連線)時,我們無需去修改那些使用物件的既有業務邏輯程式碼。我們只需要建立一個新的產品類別,並在工廠中註冊它即可。這使得系統能夠輕鬆地擁抱變化、擴充功能,而不會影響到現有穩定運行的部分。

最後,它大幅提升了程式碼的可讀性與可維護性。客戶端程式碼不再需要關心瑣碎的 if-else 或 switch 判斷,也不需要知道具體產品的類別名稱。程式碼的意圖變得非常清晰:Document doc = Factory.getDocument("pdf"); 這行程式碼的意圖就是「我需要一個 PDF 文件」,而不是「如果是 PDF 字串,我就去 new 一個 PdfDocument 物件」。這種高層次的抽象讓程式碼更貼近業務邏輯,使得新加入的開發者能更快地理解系統架構,也讓長期的維護工作變得更加輕鬆。

增加各程式獨立性、增加延展性、並且分離每個功能步驟


上一篇
Day10-UI/UX的幾個測試方式/頭腦風暴
下一篇
Day12-Code design我遇到比較常見的patten/適配器模式 (Adapter Pattern)
系列文
一條龍的軟體開發到維護,從校園工讀到職場工程師13
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言