細品 javascript 設計模式(策略模式)

我儘可能用最少的文字,最少的篇幅,講明白設計模式的方方面面。
全文鏈接javascript

理解策略模式

把 算法 和 調用算法 的部分作拆分開

我舉個例子吧:你想要去三亞旅遊,途徑有不少種:飛機,火車,自駕遊。這幾種方法均可以到達目的地,可是過程是有所不一樣的。css

  • 飛機:適合緊急不差錢的狀況
  • 火車:適合不緊急,而且目的明確(公司團建,中老年旅遊)
  • 自駕遊:這種比較隨性,和朋友家人一塊兒出門,一塊兒欣賞路過的風景。

每一種選擇,都是一種策略。
在程序中策略的意義在於,把處理不一樣任務但目的相同的代碼整合在一塊兒。再用一層函數委託他們來處理具體的算法。這樣能夠消除本來程序中大片的條件分支語句html

上代碼: 策略模式(計算不一樣的旅行方式到達時間)java

// 定義策略
var strategies = {
    'plane': function(distance) {
        return distance * 1; // 假設飛機的速度最快
    },
    'train': function(distance) {
        return distance * 4; // 飛機的速度是火車的4倍
    },
    'roadTrip': function(distance) {
        return distance * 10; // 飛機的速度是自駕遊的10倍
    },
}

// Context
var calculateBonus = function(mode, distance) {
    if (typeof strategies[mode] === 'function') {
        return strategies[mode](distance);
    }
    return -1;
}

// 調用策略
console.log(calculateBonus('plane', 1000));
console.log(calculateBonus('train', 1000));
console.log(calculateBonus('roadTrip', 1000));
策略模式是比較好理解的,我們先看一段即將被策略模式改造的代碼

使用策略模式以前

var calculateBonus = function(mode, distance) {
    if (mode === 'plane') {
        return distance * 1;
    } else if (mode === 'train') {
        return distance * 4;
    } else if (mode === 'roadTrip') {
        return distance * 10;
    }
    return -1;
}

這段代碼最大的問題是, 代碼可複製性差, 不利於維護。每次有新的改動都必須扒開代碼,找到具體的某個函數去修改。效率低,容易引起連貫性錯誤。css3

使用策略模式,實現緩懂動畫函數

爲了更加明確策略模式的使用場景,咱們一塊兒來實現一個動畫函數。

js 動畫原理改變 dom 的 css 屬性,好比 left, top, background-position。因此至少要提供一下一些信息。
97_bashen.gifweb

  • dom 最初的位置
  • 目標位置
  • 開始時間
  • 結束時間

而後配合定時器 setInterval 在定時器中每一個 19 毫秒改變一次 dom 的 css 屬性,每次修改 dom 時把上面的4個參數傳給算法。算法會計算出當前應該所在的位置。最後在更新 dom 的 css 屬性。這樣動畫就完成了。算法

算法部分,這裏最初來自 Flash 但如今 css3 部分也是這樣實現的設計模式

在線體驗bash

// 先定義動畫緩動算法
var tween = {
    linear: function(t, b, c, d) {
        return c * t / d + b;
    },
    easeIn: function(t, b, c, d) {
        return c * (t /= d) * t + b;
    },
    strongEaseIn: function(t, b, c, d) {
        return c * (t /= d) * t * t * t * t + b;
    },
    strongEaseOut: function(t, b, c, d) {
        return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
    },
    sineaseIn: function(t, b, c, d) {
        return c * (t /= d) * t * t + b;
    },
    sineaseOut: function(t, b, c, d) {
        return c * ((t = t / d - 1) * t * t + 1) + b;
    }
}

而後在 body 中添加入下節點dom

<div style="position: absolute; background:yellow;">im div</div>

接下來定義動畫類

let Animate = function(dom) {
    this.dom = dom;
    this.startTime = 0;
    this.startPos = 0;
    this.endPos = 0;
    this.propertyName = null;
    this.esing = null;
    this.duratin = null;
}
Animate.prototype.start = function(propertyName, endPos, duratin, esing) {
    this.startTime = Date.now();
    this.startPos = this.dom.getBoundingClientRect()[propertyName];
    this.propertyName = propertyName;
    this.endPos = endPos;
    this.duratin = duratin;
    this.esing = tween[esing];
    let self = this;
    let timeId = setInterval(function() {
        if (self.step() === false) {
            clearInterval(timeId)
        }
    }, 19)
}
Animate.prototype.step = function() {
    var t = Date.now();
    if (t >= this.startTime + this.duratin) {
        this.update(this.endPos)
        return false
    }
    var pos = this.esing(
        t - this.startTime, // 時間
        this.startPos, // 開始值
        this.endPos - this.startPos, // 運動距離
        this.duratin // 總耗時
    )
    this.update(pos);
}
Animate.prototype.update = function(pos) {
    this.dom.style[this.propertyName] = pos + 'px';
}

來測試一下!

var div = document.getElementsByTagName('div')[0];
var animate = new Animate(div);

animate.start('left', 500, 1000, 'linear')
// animate.start('left', 500, 1000, 'easeIn')
// animate.start('left', 500, 1000, 'strongEaseIn')
// animate.start('left', 500, 1000, 'strongEaseOut')
// animate.start('left', 500, 1000, 'sineaseIn')
// animate.start('left', 500, 1000, 'sineaseOut')

web_access_2.png

相關文章
相關標籤/搜索