iT邦幫忙

2026 iThome 鐵人賽

0
ChatGPT & Codex

從 Prompt Engineering 到 Copilot / Codex 實務 30 天系列 第 14 篇

Day 14:【物件導向與設計模式】讓 AI 示範:將混亂程式碼重構成工廠模式與單例模式

  • 分享至 

  • xImage
  •  

昨天我們透過策略模式(Strategy Pattern)成功消除了深層的 if-else 條件地獄。但在物件導向開發中,除了「行為型模式」,另一個極容易引發混亂的是「建立型模式(Creational Patterns)」。

在專案剛起步時,我們常直接在各個業務 Controller 或 Service 中隨手 new ConcreteObject()。但隨著專案規模變大,物件的建構參數愈來愈複雜、甚至需要根據環境配置動態決定生成哪種實例時,滿地散落的 new 就會造成極高的耦合度。

今天我們就來示範:如何讓 ChatGPT 扮演物件導向設計導師,一步步將分散且硬編碼(Hard-coded)的物件建構邏輯,重構成優雅的「工廠模式(Factory Pattern)」與「執行緒安全的單例模式(Singleton Pattern)」!


一、設計模式重構專用提示詞模板(Design Pattern Prompting)

當你發現系統中物件的建立邏輯開始變得臃腫、需要抽離時,可以使用以下提問結構:

【設計模式重構提示詞範本】
請扮演資深 Java 軟體架構師。以下是我目前建立與管理物件的實作片段:

[貼上既有的代碼]

請依序進行架構指導:

  1. 耦合痛點分析:直接使用 new 或粗糙實例化會造成哪些維護與單元測試(Mocking)上的問題?
  2. 工廠模式導入:如何設計工廠方法(Factory Method)或抽象工廠,將「物件的使用者」與「物件的建立者」徹底解耦?
  3. 單例模式安全考量:若該工廠或配置管理器全域只需單一實例,如何實踐執行緒安全且具備延遲載入(Lazy Loading)的最佳單例實作?
  4. Java 17+ 實踐範例:給出標準乾淨的類別設計,並說明如何呼叫使用。

二、實戰案例:各類通知發送器(Notification Sender)

假設我們有一個通知模組,需要根據使用者偏好發送「Email、SMS 簡訊、或 Slack Webhook」:

[重構前:業務邏輯與建構細節強耦合]

public class NotificationService {
    public void notifyUser(String channel, String recipient, String message) {
        if ("EMAIL".equalsIgnoreCase(channel)) {
            // 需要配置 SMTP host、port、auth 認證資訊
            EmailSender sender = new EmailSender("smtp.office365.com", 587, "user", "pass");
            sender.send(recipient, message);
        } else if ("SMS".equalsIgnoreCase(channel)) {
            // 需要配置電信商 API key 與發送端代號
            SmsSender sender = new SmsSender("API_KEY_98765", "TAIWAN_TELECOM");
            sender.send(recipient, message);
        } else {
            throw new IllegalArgumentException("未支援的通知渠道: " + channel);
        }
    }
}

痛點:每次呼叫都要重新 new 一次、配置參數散落各處,且呼叫端必須清楚知道每個 Sender 需要哪些參數。


三、AI 的重構解答:工廠模式 + 單例註冊表

AI 會帶領我們拆解為三層架構:

1. 定義共用介面

[共用介面定義]

public interface MessageSender {
    void send(String recipient, String message);
}

public class EmailSender implements MessageSender {
    public void send(String recipient, String message) {
        System.out.println("【Email】寄送給 " + recipient + ",內容:" + message);
    }
}

public class SmsSender implements MessageSender {
    public void send(String recipient, String message) {
        System.out.println("【SMS】簡訊傳送給 " + recipient + ",內容:" + message);
    }
}

2. 工廠類別結合安全單例(Bill Pugh Singleton)

AI 推薦使用靜態內部類別(Static Inner Class)方式實作單例模式。這種做法不需使用 synchronized 關鍵字,既保證了執行緒安全,又能享有延遲加載(Lazy Initialization)的效能優勢:

[工廠類別與安全單例實作]

import java.util.HashMap;
import java.util.Map;

public class NotificationFactory {

    private final Map<String, MessageSender> senderRegistry = new HashMap<>();

    // 私有建構子,防止外部隨意 new
    private NotificationFactory() {
        // 在工廠內部統一管理複雜的建構與參數注入
        senderRegistry.put("EMAIL", new EmailSender());
        senderRegistry.put("SMS", new SmsSender());
    }

    // Bill Pugh 靜態內部類別單例實作
    private static class SingletonHelper {
        private static final NotificationFactory INSTANCE = new NotificationFactory();
    }

    public static NotificationFactory getInstance() {
        return SingletonHelper.INSTANCE;
    }

    // 工廠獲取方法
    public MessageSender getSender(String channel) {
        MessageSender sender = senderRegistry.get(channel.toUpperCase());
        if (sender == null) {
            throw new IllegalArgumentException("不支援的通知頻道:" + channel);
        }
        return sender;
    }
}

四、重構後的業務調用端體驗

現在,我們的商業邏輯類別變得無比清爽:

[重構後簡潔的業務代碼]

public class NotificationService {
    public void notifyUser(String channel, String recipient, String message) {
        // 透過單例工廠取得發送器,完全不用關心它是如何被 new 出來的
        MessageSender sender = NotificationFactory.getInstance().getSender(channel);
        sender.send(recipient, message);
    }
}

五、AI 指出的架構核心收穫

  1. 隱藏複雜物件建構細節:
    業務層只需要知道「向誰要物件」,不再需要知道那個物件需要哪些配置金鑰、網路 Port 號或複雜的初始化依賴。
  2. 避免重複建立無狀態物件:
    像 EmailSender 這類無狀態(Stateless)的工具物件,如果每次發送都 new 一個,在高併發時會引發頻繁的記憶體配置與 GC。配合單例與快取註冊表,能大幅降低伺服器負載。
  3. 單元測試更加友善:
    在撰寫單元測試時,我們可以直接 Mock MessageSender 或將 Mock 物件註冊進工廠,不需要模擬真實的外部 SMTP / SMS 伺服器連線。

今日小結與預告

今天我們透過 AI 的逐步引導,學會了如何運用工廠模式抽離物件建構細節,並透過 Java 靜態內部類別實現最優雅、高效的執行緒安全單例模式。


上一篇
Day 13:【重構優化】消除 Code Smell:利用 AI 重構冗長條件句(If-Else 地獄)
下一篇
Day 15:【註解與文件】告別手寫文件:自動為既有代碼生成標準 JavaDoc 與技術文件
系列文
從 Prompt Engineering 到 Copilot / Codex 實務 30 天 共 18 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言