這裏工廠模式分爲2類:簡單工廠 和 工廠方法,下一節會介紹第3類工廠模式:抽象工廠。javascript
簡單工廠:定義一個類來建立其餘類的實例,根據參數的不一樣返回不一樣類的實例,一般這些類擁有相同的父類。java
假設如今有 3 款車,Benz、Audi 和 BMW,他們都繼承自父類 Car,而且重寫了父類方法 drive:設計模式
class Car { drive () { console.log('Car drive'); } } class Benz extends Car { drive () { console.log(`Benz drive`) } } class Audi extends Car { drive () { console.log(`Audi drive`) } } class BMW extends Car { drive () { console.log(`BMW drive`) } }
咱們定義了一個父類 Car,包含一個方法 drive,Benz、Audi 和 BMW 繼承自共同的父類 Car。函數
那麼咱們在實例化這 3 款車的時候,就須要以下調用:優化
let benz = new Benz(); let audi = new Audi(); let bmw = new BMW(); benz.drive(); // Benz drive audi.drive(); // Audi drive bmw.drive(); // BMW drive
這種寫法就很繁瑣,這時候就用到咱們的簡單工廠了,提供一個工廠類:spa
class SimpleFactory { static getCar (type) { switch (type) { case 'benz': return new Benz(); case 'audi': return new Audi(); case 'bmw': return new BMW(); } } }
簡單工廠類 SimpleFactory 提供一個靜態方法 getCar,咱們再實例化 3 款車的時候,就變成下面這樣了:設計
let benz = SimpleFactory.getCar('benz'); let audi = SimpleFactory.getCar('audi'); let bmw = SimpleFactory.getCar('bmw'); benz.drive(); // Benz drive audi.drive(); // Audi drive bmw.drive(); // BMW drive
這麼一看,使用簡單工廠後代碼行數反而變多了,這種寫法真的有優點嗎?代理
咱們要知道,設計模式並非爲了減小代碼行數而出現的,它是爲了使咱們的代碼更好擴展,更好維護,更方便使用而出現的。那麼使用簡單工廠後,有什麼好處呢?code
簡單工廠,用戶不須要知道具體產品的類名,只須要知道對應的參數便可,對於一些複雜的類名,能夠減小用戶的記憶量,同時用戶無需瞭解這些對象是如何建立及組織的,有利於整個軟件體系結構的優化。對象
因此,使用簡單工廠,是將類的實例化交給工廠函數去作,對外提供統一的方法。咱們要養成一個習慣,在代碼中 new 是一個須要慎重考慮的操做,new 出現的次數越多,代碼的耦合性就越強,可維護性就越差,簡單工廠,就是在上面作了一層抽象,將 new 的操做封裝了起來,向外提供靜態方法供用戶調用,這樣就將耦合集中到了工廠函數中,而不是暴露在代碼的各個位置。
簡單工廠有它的好處,必然也有它的缺點,好比下面這種狀況:
咱們又新增了一類車 Ferrai,就須要在簡單工廠類 SimpleFactory 中再新增一個 case,添加 Ferrari 的實例化過程。每新增一類車,就要修改簡單工廠類,這樣作其實違背了設計原則中的開閉原則:對擴展開放,對修改封閉,同時若是每類車在實例化以前須要作一些處理邏輯的話,SimpleFactory 會變的愈來愈複雜。
因此簡單工廠適用於產品類比較少而且不會頻繁增長的狀況,那麼有什麼方法能解決簡單工廠存在的問題呢?
工廠方法模式是簡單工廠的進一步優化,咱們再也不提供一個統一的工廠類來建立全部的對象,而是針對不一樣的對象提供不一樣的工廠,也就是說每一個對象都有一個與之對應的工廠。
工廠方法模式又稱爲工廠模式,它的實質是「定義一個建立對象的接口,但讓實現這個接口的類來決定實例化哪一個類,工廠方法讓類的實例化推遲到子類中執行」。
光看概念仍是很難理解,咱們沿着上面的簡單工廠來作修改,舉例說明。
仍是先定義一個父類 Car,提供一個方法 drive:
class Car { drive () { console.log('Car drive'); } }
每一款車都繼承自父類 Car,並重寫方法 drive:
class Benz extends Car { drive () { console.log(`Benz drive`) } } class Audi extends Car { drive () { console.log(`Audi drive`) } } class BMW extends Car { drive () { console.log(`BMW drive`) } }
而後按照定義,咱們提供一個建立對象的接口:
class IFactory { getCar () { throw new Error('不容許直接調用抽象方法,請本身實現'); } }
每款車都提供一個工廠類,實現自上述接口,由於 JavaScript 中沒有 implements,因此用 extends 代替:
class BenzFactory extends IFactory { getCar () { return new Benz(); } } class AudiFactory extends IFactory { getCar () { return new Audi(); } } class BMWFactory extends IFactory { getCar () { return new BMW(); } }
這樣當咱們須要實例化每款車的時候,就按以下操做:
let benzFactory = new BenzFactory(); let benz = benzFactory.getCar(); let audiFactory = new AudiFactory(); let audi = audiFactory.getCar(); let bmwFactory = new BMWFactory(); let bmw = bmwFactory.getCar(); benz.drive(); // Benz drive audi.drive(); // Audi drive bmw.drive(); // BMW drive
咱們再來對比一下簡單工廠的實例化過程:
let benz = SimpleFactory.getCar('benz'); let audi = SimpleFactory.getCar('audi'); let bmw = SimpleFactory.getCar('bmw'); benz.drive(); // Benz drive audi.drive(); // Audi drive bmw.drive(); // BMW drive
咱們用 UML 類圖來描述一下工廠方法模式:
能夠看到,一樣是爲了實例化一個對象,怎麼就變得這麼複雜了呢····咱們來總結一下工廠方法模式的步驟:
那麼工廠方法增長了如此多的流程,提升了複雜度,究竟解決了簡單工廠的什麼問題呢?
經過上文能夠知道,簡單工廠在新增一款車的時候,須要修改簡單工廠類 SimpleFactory,違背了設計模式中的「開閉原則」:對擴展開放,對修改封閉。
當工廠方法須要新增一款車的時候,好比 Ferrari,只須要定義本身的產品類 Ferrari 以及本身的工廠類 FerrariFactory:
class Ferrari extends Car { drive () { console.log(`Ferrari drive`) } } class FerrariFactory extends IFactory { getCar () { return new Ferrari(); } } let ferrariFactory = new FerrariFactory(); let ferrari = ferrariFactory.getCar(); ferrari.drive(); // Ferrari drive
徹底不用修改已有的抽象接口 IFactory,只須要擴展實現本身須要的就能夠了,不會影響已有代碼。這就是對擴展開放,對修改封閉。
簡單工廠模式
工廠方法模式