工廠方法是建立型模式,它提供一個物件建立方法,讓客戶不需透過具體類別就能建立物件。
想像你在路邊的自動販賣機買飲料,只要選取想要的品項,再投入對應的金額,自動販賣機便會給你想要的商品。工廠方法模式就像一台自動販賣機一樣,只要告訴它你要什麼,它就會生產對應的產品給你。你不需要知道這些商品如何製作,來自哪裡,自動販賣機自會處理好一切。
幾天前,老闆談到了一個新案子。這次客戶來自美容產業,他們非常重視企業形象,甚至提供了一套設計系統,要求網頁中的所有元素都符合這套規範。為了滿足客戶的需求,你提議為他們量身打造一套元件庫,其中必須包含所有常用的 UI 元件,如按鈕、輸入框和下拉選單。
幸運的是,你的同事中有一位切版高手,他只用了幾天時間就把所有元件都製作完成。然而,這些元件多達數十個,要讓每位成員熟記這些元件的命名和路徑得花上好一段時間,可能比製作元件的時間還長。主管要求你想出一個方法來統一元件的建立方式。
這有什麼難的呢?只要替每個元件取一個名稱,再透過元件名稱指定要建立的元件即可。我們還可以建立元件對照表避免永無止盡的 if else 判斷式。就這樣,你輕鬆地完成了主管交辦的任務,而主管也對你的表現非常滿意。
const beautyComponentMap = {
button: BeautyButton,
input: BeautyInput,
menu: BeautyMenu,
// ...
};
function createBeautyComponent(type: keyof typeof beautyComponentMap) {
return new beautyComponentMap[type]();
}
const button = createBeautyComponent("button");
const input = createBeautyComponent("input");
在那之後,案子順利地結束,客戶對交付的成果十分滿意。幾個月後,老闆宣布了一個新案子。這次的客戶是一家電動車代理商,他們希望我們打造一個充滿現代感的官方網站。
由於上次打造元件庫的經驗非常成功,主管決定如法炮製,為客戶製作一套現代感十足的元件庫。雖然需要打造全新的元件庫,但我們有許多程式碼可以重複利用。比如說,我們可能需要設定元件的屬性或將元件添加至網頁中。我們可以使用工廠模式來封裝通用的程式碼,然後由子類別來決定要產生哪些元件。
工廠方法模式在父類別中定義了物件的建立方法,並讓子類別決定要建立什麼物件、如何建立。使用工廠模式的子類別不僅能實現自訂的建立方法,還能使用父類別的通用方法。
我們來使用工廠方法改寫美容元件庫。首先,定義元件庫的通用介面 UILibrary
,接著再繼承 UILibrary
實作美容元件庫。
abstract class UILibrary<
ComponentMap extends Record<string, new () => Component>
> {
instantiate<T extends keyof ComponentMap>(type: T, options?: {}) {
const component = this.create(type);
component.setOptions(options);
return component;
}
abstract create<T extends keyof ComponentMap>(
type: T
): InstanceType<ComponentMap[T]>;
}
class BeautyUILibrary extends UILibrary<BeautyComponentMap> {
create<T extends keyof BeautyComponentMap>(type: T) {
const Component = beautyComponentMap[type];
return new Component() as InstanceType<BeautyComponentMap[T]>;
}
}
如此一來,美容元件庫既能擁有自訂的元件建立方法,還可以使用定義在父類別的通用方法,往後我們也能透過繼承輕鬆地打造更多元件庫。
文中分享了兩段範例程式碼,第一段程式碼是簡單工廠模式,第二段則使用了工廠方法模式。簡單工廠模式將物件的建立過程封裝起來以簡化客戶端的程式碼;工廠模式則透過繼承提供更有彈性的設計方式。這兩種模式皆有助於簡化客戶端的程式碼,前者提供簡單直覺的解決方案,後者則適合運用在更為複雜的設計情境。
工廠方法模式在父類別中定義工廠方法,由子類別實作具體細節。父類別負責提供建立物件的介面或預設方法,而子類別則根據需求決定要建立哪些物件以及如何建立。
由於工廠方法封裝了物件的建立細節,客戶只需使用工廠方法建立物件,而不需知道物件如何被建立的。這不僅簡化了客戶端的程式碼,還有助於統一物件的建立邏輯。