開個新坑涨颜,復(fù)習(xí)基礎(chǔ)知識,用typescript寫寫舊技術(shù)——設(shè)計(jì)模式依沮。今天就介紹一下工廠模式涯贞,以及其他兩個衍生模式工廠方法和抽象工廠枪狂。
簡單工廠
工廠模式,又稱簡單工廠宋渔,顧名思義就是使用“工廠”(一個或一系列方法)去生產(chǎn)“產(chǎn)品”(一個或一系列的派生類實(shí)例)州疾。UML如下所示:
上圖我定義產(chǎn)品接口名為Vegetable,它的兩個派生類為Onion和Garlic:
// Vegetable.ts
interface Vegetable {
fry(): void;
}
class Onion implements Vegetable {
public fry(): void {
console.log('Onion');
}
}
class Garlic implements Vegetable {
public fry(): void {
console.log('Garlic');
}
}
“蔬菜工廠”如下所示皇拣,通過調(diào)用不同的方法生產(chǎn)出不同的蔬菜實(shí)例严蓖。
p.s. 有些工廠模式的實(shí)現(xiàn)只有一個函數(shù)體,根據(jù)特定參數(shù)來“生產(chǎn)”特定的派生實(shí)例审磁,這也是可行的谈飒。
// SimpleFactory.ts
import {Garlic, Onion, Vegetable} from './Vegetable';
class SimpleFactory {
public createOnion(): Vegetable {
return new Onion();
}
public createGarlic(): Vegetable {
return new Garlic();
}
}
最后看一下client調(diào)用:
// client.ts
import {SimpleFactory} from './SimpleFactory';
import {Vegetable} from './Vegetable';
const factory: SimpleFactory = new SimpleFactory();
let onion: Vegetable = factory.createOnion();
let garlic: Vegetable = factory.createGarlic();
onion.fry(); // Onion
garlic.fry(); // Garlic
OK,問題來了态蒂,我們使用工廠模式的意義是什么杭措?饒了這么一大圈不就是打印了個兩個菜名嗎?為什么不直接new呢钾恢?
import {Onion, Garlic} from './Vegetable';
let onion: Onion = new Onion();
let garlic: Garlic = new Garlic();
直接new出兩個蔬菜實(shí)力確實(shí)特別簡單手素,但是在大型軟件開發(fā)中這很危險(xiǎn),用專業(yè)術(shù)語來說是違反了依賴倒置原則:
高層模塊不應(yīng)該依賴于低層模塊瘩蚪,二者都應(yīng)該依賴于抽象泉懦;抽象不應(yīng)該依賴于細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴于抽象
有點(diǎn)繞疹瘦,陽春白雪和者必寡崩哩,我們還是走下里巴人路線吧。通俗來講言沐,引入的依賴并不可靠邓嘹,它一般是第三方庫或是其他開發(fā)人員實(shí)現(xiàn)的代碼;在不確定的某一天险胰,里面的代碼會被修改甚至是刪除汹押。這時(shí)候你的代碼會變得很脆,不知不覺中就崩了起便。但現(xiàn)實(shí)中又不可能不引入其他依賴棚贾,所以大家就約定最小依賴引入:依賴提供者將隱藏對象的屬性和實(shí)現(xiàn)細(xì)節(jié),僅對外公開接口(抽象)榆综,這就是OOP三大特性之一的封裝妙痹。
再回看一下client實(shí)現(xiàn),只import了一個公用接口Vegetable
鼻疮。運(yùn)行時(shí)细诸,我們利用多態(tài)——let onion: Vegetable = factory.createOnion()
——就可以實(shí)現(xiàn)派生方法的動態(tài)綁定(onion.fry()
)。這樣陋守,“派生蔬菜”(Onion或Garlic)的修改就不會影響現(xiàn)有代碼了震贵。哪天VegetableFactory
開發(fā)者覺得Onion子類實(shí)現(xiàn)太過丑陋或是性能太差,他只需在簡單工廠里換一個新的“派生蔬菜”(OnionFromJapan
)即可水评,client代碼不需要做任何改動猩系。
class VegetableFactory {
public createOnion(): Vegetable {
return new OnionFromJapan();
}
}
工廠模式的最大優(yōu)點(diǎn)就是屏蔽產(chǎn)品的具體實(shí)現(xiàn),調(diào)用者只關(guān)心產(chǎn)品的接口中燥。當(dāng)然寇甸,它也有自己的問題,產(chǎn)品種類可能有成千上萬疗涉,如果都是依靠同一個工廠生產(chǎn)拿霉,那么必然會使得工廠代碼及其龐大。這就有了工廠方法的設(shè)計(jì)實(shí)現(xiàn)咱扣。
工廠方法
工廠方法就是針對每一種產(chǎn)品提供一個工廠類绽淘,通過不同派生工廠創(chuàng)建不同的產(chǎn)品。
實(shí)現(xiàn)很簡單闹伪,為每種蔬菜提供對應(yīng)的派生“蔬菜工廠”就行了沪铭。問題又來了,工廠與蔬菜一一對應(yīng)有沒有多此一舉呢偏瓤?要不直接new杀怠?嗯,這里不給出解答了厅克,回去體會一下oop三大特性:封裝赔退、多態(tài)和繼承。
import {Garlic, Onion, Vegetable} from './Vegetable';
interface VegetableFactory {
create(): Vegetable;
}
class OnionFactory implements VegetableFactory {
public create(): Vegetable {
return new Onion();
}
}
class GarlicFactory implements VegetableFactory {
public create(): Vegetable {
return new Garlic();
}
}
工廠方法減輕了工廠類的負(fù)擔(dān)证舟,新增一種“蔬菜”只需添加一個特定的“蔬菜工廠”即可硕旗,這就符合了開放閉合原則:
對擴(kuò)展是開放的;對修改是關(guān)閉的
這里提一下褪储,開放閉合原則并不是說接口一成不變卵渴,它要求的是增量變化——只增加新方法,不改動舊方法鲤竹。
抽象工廠
工廠方法自然比簡單工廠更加靈活浪读,但當(dāng)業(yè)務(wù)有所變更時(shí),比如需要添加“蔬菜”的周邊產(chǎn)品——“酒”呢辛藻?(洋蔥和紅酒更配哦)這時(shí)候我們就需要一個更復(fù)雜的模式——抽象工廠了碘橘。
抽象工廠是一個產(chǎn)品簇的概念,一個工廠可以生產(chǎn)多種業(yè)務(wù)相關(guān)的產(chǎn)品吱肌。我們在工廠方法的基礎(chǔ)上擴(kuò)充一下代碼:定義一個抽象工廠接口AbstractFactory
痘拆,通過不同的方法生產(chǎn)出一個“抽象”產(chǎn)品簇(Vegetable
和Drink
)〉回過頭來再看工廠方法纺蛆,事實(shí)上它就是抽象工廠最簡單的一種場景設(shè)計(jì)——只生成一種產(chǎn)品吐葵。
interface AbstractFactory {
create(): Vegetable;
pick(): Drink;
}
class OnionRecipe implements AbstractFactory {
public create(): Vegetable {
return new Onion();
}
public pick(): Drink {
return new Wine();
}
}
抽象工廠的缺點(diǎn)很明顯:成也產(chǎn)品簇?cái)∫伯a(chǎn)品簇,復(fù)雜度大桥氏,應(yīng)用場景有限温峭。
總結(jié)
簡單工廠:調(diào)用者只需使用單例工廠就可獲取同一范疇的所有產(chǎn)品
工廠方法:調(diào)用者并不知道它在運(yùn)行時(shí)會獲取何種產(chǎn)品,只知道某個特定的工廠能生成出滿足需求的產(chǎn)品
抽象工廠:調(diào)用者可以在運(yùn)行時(shí)從特定的工廠中獲得所有信息相關(guān)的產(chǎn)品簇
設(shè)計(jì)模式是上個世紀(jì)九十年代初從建筑領(lǐng)域引入到計(jì)算機(jī)軟件開發(fā)領(lǐng)域的概念字支;是對軟件設(shè)計(jì)中反復(fù)出現(xiàn)的各種問題所提出的一套解決方案凤藏。一般來說,設(shè)計(jì)模式的教學(xué)案例都是基于OOP語言java實(shí)現(xiàn)的堕伪,所以有時(shí)候會有一種錯覺揖庄,以為設(shè)計(jì)模式只有java開發(fā)人員才該掌握的。但事實(shí)上設(shè)計(jì)模式更多的是一種思想欠雌,是在某種情景下解決特定問題的可靠途徑蹄梢,它不僅僅局限于組織編碼,更可以應(yīng)用于架構(gòu)層面的思考桨昙。
思考題
如果我們的一個服務(wù)類中出現(xiàn)大量類似于if(useA)
检号、if(useB)
這樣的條件判斷語句,你會怎么去重構(gòu)它蛙酪?
class Service {
public methodA(): void {
...
if( this.useA ){
// omit
}
...
}
public methodB(): void {
...
if( this.useA ){
// omit
}
...
if( this.useB ){
// omit
}
...
}
}