用JS來理解設計模式(一)

前言

最近在看Head First設計模式,想好好學一下設計模式相關的知識。這本書要求看書的人須要會java和OO(即面向對象)設計原則,好在二者在學校都學過。可是平時在寫js和業務代碼的時候不多用到相關的知識,而咱們經常使用到的庫和框架,卻無處不存在着這些知識,因此想本身寫一些庫或者框架的人,學會設計模式仍是頗有必要的。我以爲設計模式和算法有一些類似之處,它們都是一種思想,是一種解決方法。設計模式不是具體的代碼和具體的實現,而是和算法同樣,是以一種思想存在,當咱們想實現某種功能,或者構思框架和庫的時候,相關的解決方案便浮如今你的腦海裏,因此說設計模式不是說就必定是一種模式,有多是模式的組合,模式基礎上的變化。固然,以上所說也只是我看了前幾章的想法。最主要的仍是書中所寫的實現代碼皆是java,因此仍是想着用js寫一遍,畢竟js看起來不太像是拿來寫面向對象的(不過es6已經有Class了)。因此寫這個也權當作是自我記錄吧。java

策略模式

策略模式定義了算法族,分別封裝起來,讓它們之間能夠互相替換,此模式讓算法的變化獨立於使用算法的客戶。

看了這個正式定義後多是很差理解的,不過不要緊,講個書裏的例子就明白了。es6

小李的公司要求他開發一套模擬鴨子的遊戲,所以他寫了個超類(即父類)Duck,並讓各類鴨子繼承此超類。(如下代碼皆用js來編寫)算法

class Duck {
    quack () {} 
    swim () {}
    display () {}
}

他給鴨子超類定義了鴨子叫、游泳、展現方法。可是因爲每一種鴨子的外觀不一樣,所以在這裏的定義是抽象方法(即在超類中不實現,交由子類負責實現,但在js中並無這種定義),而後在每一個鴨子子類中進行實現本身的display方法。編程

就這樣,小李完成了一個簡單的模擬鴨子游戲。可是,次日,產品經理讓小李給鴨子加一個飛行的方法。小李心想這還不簡單嗎,在Duck超類里加一個fly () {}方法就完事了,這就是面向對象的威力。設計模式

然而,在次日的演示中,產品經理驚訝的發現‘橡皮鴨子’也能在屏幕飛來飛去。小李心想糟糕了,因而趕忙在橡皮鴨的類中進行了fly方法覆蓋,雖然暫時解決了問題,可是一想到往後還要添加各類各樣的鴨子,每隻️鴨子的飛行方法和叫聲都不同,難道每次添加新類的時候都要去覆蓋和檢查嗎?框架

設計原則: 找出應用中可能須要變化的地方,而後將它們獨立出來,不要與那些不須要變化的代碼混在一塊兒。

根據這個原則來分析鴨子的行爲,就目前來看,鴨子叫和飛行方法是會隨着鴨子的不一樣而改變的,所以須要將兩個方法取出來,創建新的類來表明行爲。那爲何不取出display呢?這件事書裏並無提到,可是稍稍分析便知道了,display對於每一個鴨子子類來講都是一定須要設置的,又不存在相同的狀況,所以沒有潛在的複用可能性,就是獨立成display類也仍是依附於特定鴨子子類的,所以沒有必要將展現方法取出來。this

設計原則: 針對接口編程,而不是針對實現編程。

這裏的接口指的是面向對象裏的一個關鍵字interface,接口就像是一個定義好了全部方法的類,可是其中的每個方法都不進行實際代碼的實現,而是交由繼承它的子類來實現方法。它是一種約束,繼承了它的子類都必須實現。所以,當咱們在使用接口的時候,咱們能確定的去使用繼承了某個接口的類的方法,即就是方法必然存在。所以在編寫的時候無需關心具體的代碼實現(以上是我的的理解)。設計

所以書中分別建了FlyBehaviorQuackBehavior這兩個接口,不過js中並無接口這種定義,可是js中並無與接口功能相似的方法存在,只能寫個類去代替它,可是這個類並無約束效力,所以從這裏也能夠看出來TS的誕生緣由。code

class FlyBehavior {
    fly () {
        console.log('未定義飛行方法');
    }
}

class FlyWithWings extends FlyBehavior {
    fly () {
        console.log('FlyWithWings');
    }
}

class FlyNoWay extends FlyBehavior {
    fly () {
        console.log('FlyNoWay');
    }
}

QuackBehavior的實現與上面相似。orm

這樣的設計,可讓飛行和呱呱叫的動做被其餘對象複用,由於這些行爲已經與鴨子類無關了。而咱們能夠新增一些行爲,不會影響到既有的行爲類,也不會影響使用到飛行行爲的鴨子類。

此時再來重寫編寫鴨子類,就變成了以下。

class Duck {
    flyBehavior
    quackBehavior
    swim () {}
    display () {}
    setFly (fly) {
        this.flyBehavior = fly;
    }
    setQuack (quack) {
        this.quackBehavior = quack;
    }
    performFly () {
        this.flyBehavior.fly();
    }
    performQuack () {
        this.quackBehavir.quack();
    }
}

能夠看到此時咱們再也不在意具體的fly方法或者quack 方法是怎麼樣的,咱們只須要去調用方法便可,同時還能夠在運行時進行方法的替換,經過調用setQuacksetFly方法。

此時來寫一個具體的鴨子類。

class MallardDuck extends Duck {
    constructor () {
        super();
        this.setQuack(new Quack());
        this.setFly(new FlyWithWings());
    }
    display () {} // 記得重寫
}

到這裏大概差很少大概就可以理解策略模式是怎麼樣的了,有時候咱們能夠把一組行爲認爲是一族算法,而後封裝起來,互相獨立,能夠隨意替換,這差很少就是策略模式的面貌了。

設計原則:多用組合,少用繼承。
相關文章
相關標籤/搜索