在現實生活中,工廠是負責生產產品的,比如牛奶、麵包或禮物等,這些產品滿足了我們日常的生理需求。此外,在日常生活中,我們也離不開大大小小的系統,這些系統是由不同的元件物件構成。
而作為一名 Web 軟體開發工程師,在軟體系統的設計與開發過程中,我們可以利用設計模式來提高程式碼的可重用性、可擴充套件性和可維護性。在眾多設計模式當中,有一種被稱為工廠模式的設計模式,它提供了建立物件的最佳方式。
工廠模式可以分為三類:
- 簡單工廠模式(Simple Factory Pattern)
- 工廠方法模式(Factory Method Pattern)
- 抽象工廠模式(Abstract Factory Pattern)
本文阿寶哥將介紹簡單工廠模式與工廠方法模式,而抽象工廠模式將在後續的文章中介紹,下面我們先來介紹簡單工廠模式。
一、簡單工廠模式
1.1 簡單工廠模式簡介
簡單工廠模式又叫 靜態方法模式,因為工廠類中定義了一個靜態方法用於建立物件。簡單工廠讓使用者不用知道具體的引數就可以建立出所需的 ”產品“ 類,即使用者可以直接消費產品而不需要知道產品的具體生產細節。
相信對於剛接觸簡單工廠模式的小夥伴來說,看到以上的描述可能會覺得有點抽象。這裡為了讓小夥伴更好地理解簡單工廠模式,阿寶哥以使用者買車為例,來介紹一下 BMW 工廠如何使用簡單工廠模式來生產?。
在上圖中,阿寶哥模擬了使用者購車的流程,pingan 和 qhw 分別向 BMW 工廠訂購了 BMW730 和 BMW840 型號的車型,接著工廠按照對應的模型進行生產並在生產完成後交付給使用者。接下來,阿寶哥將介紹如何使用簡單工廠來描述 BMW 工廠生產指定型號車子的過程。
1.2 簡單工廠模式實戰
- 定義 BMW 抽象類
abstract class BMW {
abstract run(): void;
}
- 建立 BMW730 類(BMW 730 Model)
class BMW730 extends BMW {
run(): void {
console.log("BMW730 發動咯");
}
}
- 建立 BMW840 類(BMW 840 Model)
class BMW840 extends BMW {
run(): void {
console.log("BMW840 發動咯");
}
}
- 建立 BMWFactory 工廠類
class BMWFactory {
public static produceBMW(model: "730" | "840"): BMW {
if (model === "730") {
return new BMW730();
} else {
return new BMW840();
}
}
}
- 生產並發動 BMW730 和 BMW840
const bmw730 = BMWFactory.produceBMW("730");
const bmw840 = BMWFactory.produceBMW("840");
bmw730.run();
bmw840.run();
以上程式碼執行後的輸出結果為:
BMW730 發動咯
BMW840 發動咯
通過觀察以上的輸出結果,我們可以知道我們的 BMWFactory 已經可以正常工作了。在 BMWFactory 類中,阿寶哥定義了一個 produceBMW()
方法,該方法會根據傳入的模型引數來建立不同型號的車子。
看完簡單工廠模式實戰的示例,你是不是覺得簡單工廠模式還是挺好理解的。那麼什麼場景下使用簡單工廠模式呢?要回答這個問題我們需要來了解一下簡單工廠的優缺點。
1.3 簡單工廠模式優缺點
1.3.1 優點
- 將建立例項與使用例項的任務分開,使用者不必關心物件是如何建立的,實現了系統的解耦;
- 客戶端無須知道所建立的具體產品類的類名,只需要知道具體產品類所對應的引數即可。
1.3.2 缺點
- 由於工廠類集中了所有產品建立邏輯,一旦不能正常工作,整個系統都要受到影響。
- 系統擴充套件困難,一旦新增新產品就不得不修改工廠邏輯,在產品型別較多時,也有可能造成工廠邏輯過於複雜,不利於系統的擴充套件和維護。
瞭解完簡單工廠的優缺點,我們來看一下它的應用場景。
1.4 簡單工廠模式應用場景
在滿足以下條件下可以考慮使用簡單工廠模式:
- 工廠類負責建立的物件比較少:由於建立的物件比較少,不會造成工廠方法中業務邏輯過於複雜。
- 客戶端只需知道傳入工廠類靜態方法的引數,而不需要關心建立物件的細節。
介紹完簡單工廠模式,接下來我們來介紹本文的主角 ”工廠方法模式“。
二、工廠方法模式
2.1 工廠方法簡介
工廠方法模式(Factory Method Pattern)又稱為工廠模式,也叫多型工廠(Polymorphic Factory)模式,它屬於類建立型模式。
在工廠方法模式中,工廠父類負責定義建立產品物件的公共介面,而工廠子類則負責生成具體的產品物件, 這樣做的目的是將產品類的例項化操作延遲到工廠子類中完成,即通過工廠子類來確定究竟應該例項化哪一個具體產品類。
在上圖中,阿寶哥模擬了使用者購車的流程,pingan 和 qhw 分別向 BMW 730 和 BMW 840 工廠訂購了 BMW730 和 BMW840 型號的車型,接著工廠按照對應的模型進行生產並在生產完成後交付給使用者。接下來,阿寶哥來介紹如何使用工廠方法來描述 BMW 工廠生產指定型號車子的過程。
2.2 工廠方法實戰
- 定義 BMW 抽象類
abstract class BMW {
abstract run(): void;
}
- 建立 BMW730 類(BMW 730 Model)
class BMW730 extends BMW {
run(): void {
console.log("BMW730 發動咯");
}
}
- 建立 BMW840 類(BMW 840 Model)
class BMW840 extends BMW {
run(): void {
console.log("BMW840 發動咯");
}
}
- 定義 BMWFactory 介面
interface BMWFactory {
produceBMW(): BMW;
}
- 建立 BMW730Factory 類
class BMW730Factory implements BMWFactory {
produceBMW(): BMW {
return new BMW730();
}
}
- 建立 BMW840Factory 類
class BMW840Factory implements BMWFactory {
produceBMW(): BMW {
return new BMW840();
}
}
- 生產並發動 BMW730 和 BMW840
const bmw730Factory = new BMW730Factory();
const bmw840Factory = new BMW840Factory();
const bmw730 = bmw730Factory.produceBMW();
const bmw840 = bmw840Factory.produceBMW();
bmw730.run();
bmw840.run();
通過觀察以上的輸出結果,我們可以知道我們的 BMW730Factory 和 BMW840Factory 工廠已經可以正常工作了。相比前面的簡單工廠模式,工廠方法模式通過建立不同的工廠來生產不同的產品。下面我們來看一下工廠方法有哪些優缺點。
2.3 工廠方法優缺點
2.3.1 優點
- 在系統中加入新產品時,無須修改抽象工廠和抽象產品提供的介面,只要新增一個具體工廠和具體產品就可以了。這樣,系統的可擴充套件性也就變得非常好,更加符合 “開閉原則”。而簡單工廠模式需要修改工廠類的判斷邏輯。
- 符合單一職責的原則,即每個具體工廠類只負責建立對應的產品。而簡單工廠模式中的工廠類存在一定的邏輯判斷。
- 基於工廠角色和產品角色的多型性設計是工廠方法模式的關鍵。它能夠使工廠可以自主確定建立何種產品物件,而如何建立這個物件的細節則完全封裝在具體工廠內部。工廠方法模式之所以又被稱為多型工廠模式,是因為所有的具體工廠類都具有同一抽象父類。
2.3.2 缺點
- 在新增新產品時,需要編寫新的具體產品類,而且還要提供與之對應的具體工廠類,系統中類的個數將成對增加,在一定程度上增加了系統的複雜度,有更多的類需要編譯和執行,會給系統帶來一些額外的開銷。
- 一個具體工廠只能建立一種具體產品。
最後我們來簡單介紹一下工廠方法的應用場景。
2.4 工廠方法應用場景
- 一個類不知道它所需要的物件的類:在工廠方法模式中,客戶端不需要知道具體產品類的類名,只需要知道所對應的工廠即可,具體的產品物件由具體工廠類建立;客戶端需要知道建立具體產品的工廠類。
- 一個類通過其子類來指定建立哪個物件:在工廠方法模式中,對於抽象工廠類只需要提供一個建立產品的介面,而由其子類來確定具體要建立的物件,利用物件導向的多型性和里氏代換原則,在程式執行時,子類物件將覆蓋父類物件,從而使得系統更容易擴充套件。