《Javascript設計模式核心原理與應用實踐》筆記整理

關於設計模式, 不論是靜態類型語言,仍是像JavaScript這樣的動態類型語言,都常常出現,這些設計模式,萬變不離其宗,重要的是理念,而不是概念,全部的模式都是前人的總結,重點不是怎麼去把這些模式硬塞到項目中,而是學會如何在恰當的位置,以事半功倍的方式去使用他們。javascript

1、設計原則

設計模式基於的原則SOLID:vue

1. 單一功能原則-Single Responsibility Principlejava

2. 開放封閉原則 - Opened Closed Principlevuex

3. 裏式替換原則 - Liskov Substitution Principleredux

4. 接口隔離原則 - Interface Segregation Principle設計模式

5. 依賴反轉原則 - Dependency Inversion Principle閉包

看完並非這些都是什麼概念。。。嗯,接着往下學。app

設計模式的核心是-封裝變化。iphone

意思是: 將變與不變隔離開,不變的穩定下來,變化的部分封裝起來,確保迭代的時候改動最小。函數


23種設計模式,看的眼花,基本記不住。

建立型、結構型、行爲型。大體分爲這三種。

2、建立型1 - 工廠模式

構造函數

function Person(name, age, gender) {
    this.name = name
    this.age = age
    this.gender = gender
}複製代碼

上面的例子是構造函數,調用它的方式:

const person1 = new Person('張三', 24, '男')複製代碼

構造器模式就是用構造函數Person來初始化對象person1。

能夠發現,變的是person的屬性值,不變的是這些共性的屬性。

構造器模式是抽象了對象實例的變與不變。

工廠模式: 抽象不一樣構造函數之間的變與不變。

簡單工廠模式

上面的例子裏,穩定的是person的屬性,可是若是要添加新的屬性,給不一樣的性別描述,就要往構造函數中手動添加,那麼這個構造函數也變的相對不穩定了。

這時候,要把這個變也抽出來。

function Man(name, age) {
    this.name = name
    this.age = age
    this.gender = '男'
    this.genderDesc = '孔武有力'
}

function Woman(name, age) {
    this.name = name
    this.age = age
    this.gender = '女'
    this.genderDesc = '溫柔似水'
}

function factory(name, age, gender) {
    switch (gender) {
	case '男':
	    return new Man(name, age)
	    break
	case '女':
	    return new Woman(name, age)
	    break
    }
}複製代碼

這只是性別的描述,只判斷了兩個,若是是別的,職業、愛好...要添加在factory中的判斷就不少不少。

在上面的例子中,無論男女,都有name、age、gender、genderDesc, 值都是變的,genderDesc隨着gender的變化而變化。

function Person(name, age, gender, genderDesc) {
    this.name = name
    this.age = age
    this.gender = gender
    this.genderDesc = genderDesc
}

function factory(name, age, gender) {  
    let genderDesc  
    switch (gender) {    
        case '男':
            genderDesc = '孔武有力'
            break
        case '女':      
            genderDesc = '溫柔如水'      
            break  
    }  
    return new Person(name, age, gender, genderDesc)
}複製代碼

這樣寫好像好多了。

這就是工廠模式:

將建立對象的過程單獨封裝,目的就是爲了無腦傳參。

構造器是解決了不斷實例的問題,工廠模式解決了多個類的問題。

3、建立型2- 抽象工廠模式

什麼叫抽象類,java這種強類型的靜態語言中,會在編譯階段就對類型進行檢查,定義的是什麼類型,若是值不對,編譯的時候直接就拋出異常了,建立對象時,要關注類型之間的解耦,來保證多態性,然而在JavaScript中,自然就具備多態性,看起來好像不須要抽象類。

上面那個例子,若是是對不一樣的工做內容進行抽取判斷,不一樣的工做崗位,權限也是不同的,好比管理崗,外包,等,每次新加一個職業羣體,就得在factory中加一個判斷,會越加越多,維護起來也很複雜。

開放封閉原則: 對拓展開放,對修改封閉。意思是,實體(類、模塊、函數)能夠擴展,可是不能夠修改。

不停的添加判斷就是在修改,不是在擴展。

下面一個例子: 

手機最重要的是操做系統和硬件兩部分,手機廠商量產手機時能夠建立以下類:

class MobileFactory {
    // 操做系統接口
    createOS(){
        throw new Error("抽象工廠方法不容許直接調用,你須要將我重寫!")
    }
    // 硬件接口
    createHardWare(){
        throw new Error('抽象工廠方法不容許直接調用, 你須要將我重寫')
    }
}複製代碼

上面的類除了聲明量產手機的通用線, 定下規矩 ,啥也不幹,不能夠直接調用。在抽象工廠裏,這個類就是食物鏈頂端的抽象工廠AbstractFactory。

抽象工廠不幹活,幹活是具體工廠的事情(ConcreteFactory)。當明確一條線要生產什麼手機,具體工廠的工做就明確了,若是如今要生產一個Android+高通硬件的手機,名爲FakeStar,就能夠爲FakeStar建立一個具體工廠:

class FakeStarFactory extends MobileFactory{
    createOS(){
        // 安卓系統實例
        return new AndroidOS()
    }
    createHardWare(){
        // 高通硬件實例
        return new QualcommHardWare()
    }
}複製代碼

此處咱們調用了兩個構造函數AndroidOS和QualcommHardWare。像這樣的用來new出具體對象的類,叫具體產品類-ConcreteProduct。這種類每每不會孤立存在,不一樣具體產品每每會有相同的功能,安卓和iOS都有操做系統和硬件系統,所以咱們能夠創造一個抽象產品類-AbstractProduct,來聲明這一類產品應該具備的基本功能。

// 定義操做系統這類產品的抽象產品類
class OS {
    controlHardWare(){
        throw new Error('我不容許被直接調用')
    }
}

// 定義具體操做系統的具體實現類
class AndroidOS extends OS {
    controlHardWare() {
        console.log('我會用安卓的方式操做硬件')
    }
}

class AppleOS extends OS {
    controlHardWare(){
        console.log('我會用蘋果的方式操做硬件')
    }
}複製代碼

硬件類產品同理

// 定義硬件這類產品的抽象產品類
class HardWare {
    // 手機硬件產品的共性方法,這裏是根據命令運轉
    operateByOder() {
        thew new Error('我不容許被直接調用')
    }
}
// 定義具體硬件的具體實現類
class QualcommHardWare extends Hardware {
    operateByOrder(){
        console.log('用高通方式運轉')
    }
}

class MiHardWare extends HardWare {
    opreateByOrder(){
        console.log('用小米方式運轉')
    }
}複製代碼

這樣一來,咱們生產一部FakeStar,只須要這樣

// 手機
const iphone6 = new FakeStarFactory()
// 手機的操做系統
const myOS = iphone.createOS()
// 手機的硬件
const myHardWare = iphone.createHardWare()
// 啓動操做系統
myOS.controlHardWare()
// 喚醒硬件
myHardWare.operateByOrder()複製代碼

FakeStar過氣了,再生產別的手機,咱們也不須要對MobileFactory作任何修改,再新建一個手機種類就好了。

class FakeProFactory extends MobileFactory {
    createOS(){
        return new ...
    }
    createHardWard(){
        return new ...
    }
}複製代碼

這樣他不會對現有的模塊形成任何影響。

總結簡單工廠模式和抽象工廠模式的異同:

他們都將同與不一樣分離,場景複雜度不一樣而已。

簡單工廠模式裏,處理的對象是類,邏輯自己比較簡單,不苛求可擴展性。

抽象工廠模式裏,處理對象也是類,可是類比較複雜,類中分出了品種和等級,存在着不少可能性,須要是可悲擴展的。這時候有了四種角色:

抽象工廠-是一個抽象類,不能被用於生成具體實例-他用來聲明不一樣產品的共性,一個系統裏可能有多個抽象工廠,好比手機、平板、手提電腦等。他們統稱爲產品族。

具體工廠- 用來生產產品族中的某一個產品-繼承自抽象工廠,實現了抽象工廠中聲明的那些方法,用於建立具體產品的類。

抽象產品-抽象類,不能用於生成具體實例-上面例子中,具體產品中實現的接口會依賴一些類,這些具體產品類的共性各自抽離,對應到各自抽象產品類。

具體產品-用於生成產品族中更細粒度的產品- 好比上面的某一種操做系統,某一種硬件等。

抽象工廠就是圍繞一個超級工廠來建立其餘廠,JavaScript中用的不太多,上面的例子算是用ES6的方式來模擬Java的抽象類。

4、建立型3-單例模式

單例模式的核心: 整個程序有且只有一個實例。該類負責建立本身的對象,同時確保只有一個對象被建立。

單例模式比較簡單,在Vuex中被應用。

有且只有一個實例,這句話讓人有點困惑,通常狀況下咱們建立了一個類以後,能夠用new來實例化,想實例化幾個就實例化幾個,怎麼來確保只有一個實例呢??

也就是說單例模式想實現的是無論怎麼實例化,都要返回的是最初建立的那個實例。這須要該類(實質上是構造函數)有能力判斷本身是否已經實例化過。

class SingDog {
    show(){
        console.log('我是一個實例對象')
    }
    // 把判斷實例的邏輯放入靜態方法中,其實也能夠直接寫入構造函數的函數體內
    static getInstance(){
        // 判斷是否已經new過一個實例
        if(!SingDog.instance) {
            // 若是實例不存在,先建立他
            SingDog.instance = new SingDog()
        } else {
            // 若是已經存在實例 就直接返回
            return SingDog.instance
        }
    }
}

const instance1 = SingDog.getInstance()
const instance2 = SingDog.getInstance()
instance1 === instance2 // true複製代碼

在ES6中static關鍵字: 

定義一個類成員,使他徹底獨立於該類的任何對象
它能被類自己使用,而沒必要引用任何特定的實例。

看完定義,仍是有點抽象。

類是實例的原型,以前new 一個test(),那麼test中全部的方法都會被實例繼承,但若是在一個方法前加上static,表示該方法不會被實例繼承,而是直接經過類來調用。

父類中的靜態方法能夠被子類繼承。

或者用閉包實現getInstance

SingDog.getInstance = (function(){
    // 定義自由變量instance, 模擬私有變量
    let instance = null
    return function(){
        // 判斷instance 是否存在
        if(!instance) {
            instance = new SingDog()
        } else {
            return instance
        }
    }
})複製代碼

在getInstance攔截下,無論調多少次都只有一個實例對象。

應用:Vuex

狀態管理不論是redux仍是vuex,都實現全局Store存儲應用的全部狀態。

Vuex中使用了單一狀態樹,就是一個對象存儲了全部狀態,他是惟一一個數據源。由於用了單例模式,意味着整個應用只有一個store實例。

Vue中組件相互獨立,想要通訊,通常用props,但props只用在父子之間,複雜的好比兄弟組件通訊,就用不了props了。

這時咱們能夠把數據源放在全局,數據源一變,你們都變。

Vuex中保證store惟一性的辦法:

Vue.use(Vuex)

new Vue({
    el: '#app',
    store
})複製代碼

經過調用Vue.use()安裝了一個Vuex插件,Vuex是一個對象,他內部實現了一個install方法,此方法在插件安裝時被調用,將store注入到Vue實例中。每安裝一次,都嘗試實例一個新的store。

因此在install中就有一段判斷來攔截屢次實例化。

相關文章
相關標籤/搜索