JavaScript設計模式第2篇:工廠模式

分類

這裏工廠模式分爲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 類圖來描述一下工廠方法模式:

image.png

能夠看到,一樣是爲了實例化一個對象,怎麼就變得這麼複雜了呢····咱們來總結一下工廠方法模式的步驟:

  1. 定義產品父類 -- Car
  2. 定義子類實現父類,並重寫父類方法 -- BenzCar、AudiCar、BMWCar
  3. 定義抽象接口,以及抽象方法 -- IFactory
  4. 定義工廠類,實現自抽象接口,而且實現抽象方法 -- BenzFactory、AudiFactory、BMWFactory
  5. new 工廠類,調用方法進行實例化

那麼工廠方法增長了如此多的流程,提升了複雜度,究竟解決了簡單工廠的什麼問題呢?

經過上文能夠知道,簡單工廠在新增一款車的時候,須要修改簡單工廠類 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,只須要擴展實現本身須要的就能夠了,不會影響已有代碼。這就是對擴展開放,對修改封閉

總結

  1. 簡單工廠模式

    • 解決了用戶屢次本身實例化的問題,屏蔽細節,提供統一工廠,將實例化的過程封裝到內部,提供給用戶統一的方法,只須要傳遞不一樣的參數就能夠完成實例化過程,有利於軟件結構體系的優化;
    • 但不足之處是,增長新的子類時,須要修改工廠類,違背了「開閉原則」,而且工廠類會變得愈來愈臃腫;
    • 簡單工廠模式適用於固定的,不會頻繁新增子類的使用場景
  2. 工廠方法模式

    • 經過在上層再增長一層抽象,提供了接口,每一個子類都有本身的工廠類,工廠類實現自接口,而且實現了統一的抽象方法,這樣在新增子類的時候,徹底不須要修改接口,只須要新增本身的產品類和工廠類就能夠了,符合「開閉原則」;
    • 但不足之處也正是如此,持續的新增子類,致使系統類的個數將成對增長,在必定程度上增長了系統的複雜度,同時有更多的類須要編譯和運行,會給系統代理一些額外的開銷;
    • 工廠方法模式適用於會頻繁新增子類的複雜場景;
相關文章
相關標籤/搜索