修煉內功之JavaScript設計模式(三)

觀感度:🌟🌟🌟🌟🌟javascript

口味:黑椒牛排前端

烹飪時間:20min


想要成爲一名優秀的軟件開發人員,要具備強烈的工匠精神。遇到問題,想辦法解決問題。大多數人在前期更多的依靠的是激情,中後期則須要必定的耐心。java

工做時間久了,天然對軟件系統產生本身的思考,還會面臨職業生涯的一個挑戰。要不要成爲一個技術負責人?jquery

技術負責人固然要從更大的角度來考慮問題,那麼設計模式即是一門必修課,學習設計模式不只可以在平常的業務代碼中給咱們提供解決問題的思路。在架構上的設計也無處不見設計模式的思想。es6

這是一件高收益值、長半衰期的事,值得你堅持下去。正則表達式

話很少說,如今開飯!算法

本文所講述的是行爲型設計模式,包括模板方法模式觀察者模式狀態模式策略模式職責鏈模式命令模式訪問者模式中介者模式備忘錄模式迭代器模式解釋器模式設計模式

模板方法模式 Template Method

概念:父類中定義一組操做算法骨架,而將一些實現步驟延遲到子類中,使得子類能夠不改變父類的算法結構的同時可從新定義算法中某些實現步驟。數組

核心在於對方法的重用,將核心方法封裝在基類中,讓子類繼承基類的方法,實現基類方法的共享,達到方法公用。緩存

class canteen {
  eat () {
    eat1();
    eat2();
    eat3();
  }
  eat1 () {
    console.log('海底撈');
  }
  eat2 () {
    console.log('日本料理');
  }
  eat3 () {
    console.log('燒烤');
  }
}
複製代碼

應用實例:

一、「把大象放冰箱須要三步」是一個頂層邏輯,具體實現可能因冰箱大小有所不一樣。

二、泡咖啡和泡茶。

三、VS CodeWebstorm等一些IDE中的模板代碼片斷功能。

四、蛋糕模具

觀察者模式 Observer

概念:又被稱做發佈-訂閱模式或消息機制,定義了一種依賴關係,解決了主體對象與觀察者之間功能的耦合。

進一步說,觀察者模式定義了對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,全部依賴於它的對象都獲得通知並被自動更新。

應用實例:

1.拍賣的時候,拍賣師是觀察者觀察最高競價,而後通知給其餘競價者競價。

2.在Nodejs中經過EventEmitter實現了原生的對於這一模式的支持。

3.jquery中的trigger/on

4.MVVM

5.微信公衆號推送文章。

拿微信公衆號來舉個例子吧,大家(不一樣的觀察者)關注了前端食堂(一個主體),當公衆號文章更新時(主體更新),大家都能收到個人推送(觀察者)。

// Subject是一個公衆號主體,須要維護訂閱的觀察者數組。
// new Subject()建立一個新的公衆號
// addObserver 添加觀察者
// removeObserver 刪除觀察者
// inform 通知觀察者
function Subject() {
  this.observers = [];
}
Subject.prototype.addObserver = function (observer) {
  this.observers.push(observer);
}
Subject.prototype.removeObserver = function (observer) {
  let index = this.observers.indexOf(observer);
  if (index > -1) {
    this.observers.splice(index, 1);
  }
}
Subject.prototype.inForm = function () {
  this.observers.forEach(observer=>{
    observer.update();
  })
}
function Observer (name) {
  this.name = name;
  this.update = function () {
    console.log(name + 'update...');
  }
}

let subject = new Subject();
let observer1 = new Observer('fans1');
subject.addObserver(observer1);
let observer2 = new Observer('fans2');
subject.addObserver(observer2);
subject.inForm();

// fans1 update...
// fans2 update...
複製代碼
// 下面是ES6的寫法,感謝關注前端食堂的小婊,咳咳,小寶貝們~
class Subject {
  constructor () {
    this.observers = [];
  }
  addObserver (observer) {
    this.observers.push(observer);
  }
  removeObserver (observer) {
    let index = this.observers.indexOf(observer);
    if (index > -1) {
      this.observers.splice(index, 1);
    }
  }
  inForm () {
    this.observers.forEach(observer=> {
      observer.update();
    })
  }
}
class Observer {
  constructor (name) {
    this.name = name;
    this.update = function () {
      console.log(name + '麼麼...');
    }
  }
}
let subject = new Subject();
let observer1 = new Observer('baby1');
subject.addObserver(observer1);
let observer2 = new Observer('baby2');
subject.addObserver(observer2);
subject.inForm();

// baby1 麼麼...
// baby2 麼麼...
複製代碼

狀態模式 State

概念:當一個對象的內部狀態發生改變時,會致使其行爲的改變,這看起來像是改變了對象。

狀態模式的目的是簡化分支判斷流程。

應用實例:

1.LOL中英雄技能形成的各類狀態如沉默、減速、禁錮、擊飛等。

2.文件上傳有掃描、正在上傳、暫停、上傳成功、上傳失敗等狀態。

class HappyYasuo {
  constructor () {
    this._currentState = [];
    this.states = {
      q_key () {console.log('斬鋼閃')},
      w_key () {console.log('風之障壁')},
      e_key () {console.log('踏前斬')},
      r_key () {console.log('狂風絕息斬')}
    }
  }

  change (arr) {  // 更改當前動做
    this._currentState = arr;
    return this;
  }

  go () {
    console.log('觸發動做');
    this._currentState.forEach(T => this.states[T] && this.states[T]());
    return this;
  }
}

new HappyYasuo()
    .change(['q_key', 'w_key'])
    .go()                      // 觸發動做 斬鋼閃 風之障壁
    .go()                      // 觸發動做 斬鋼閃 風之障壁
    .change(['e_key'])
    .go()                      // 觸發動做 踏前斬
複製代碼

策略模式 Strategy

概念:將定義的一組算法封裝起來,使其相互之間能夠替換。封裝的算法具備必定獨立性,不會隨客戶端變化而變化。

應用實例:

1.諸葛亮的錦囊妙計,每個錦囊就是一個策略。

2.旅行的出遊方式,選擇騎自行車、坐汽車,每一種旅行方式都是一個策略。

class ZhaoYun {
  constructor (type) {
    this.type = type;
  }
  plan () {
    if (this.type === 'first') {
      console.log('打開第一個錦囊');
    } else if (this.type === 'second') {
      console.log('打開第二個錦囊');
    } else if (this.type === 'third') {
      console.log('打開第三個錦囊');
    }
  }
}
var u1 = new ZhaoYun('first');
u1.plan();
var u2 = new ZhaoYun('second');
u2.plan();
var u3 = new ZhaoYun('third');
u3.plan();
複製代碼
class FirstStep {
  plan () {
    console.log('打開第一個錦囊');
  }
}
class SecondStep {
  plan () {
    console.log('打開第二個錦囊');
  }
}
class ThirdStep {
  plan () {
    console.log('打開第三個錦囊');
  }
}

var u1 = new FirstStep();
u1.plan();
var u2 = new SecondStep();
u2.plan();
var u3 = new ThirdStep();
u3.plan();
複製代碼

職責鏈模式 Chain of Responsibility

概念:解決請求的發送者與請求的接受者之間的耦合,經過指責鏈上的多個對象對分解請求流程,實現請求在多個對象之間的傳遞,直到最後一個對象完成請求的處理。

應用實例:

1.JavaScript中的事件冒泡。

2.長安十二時辰中的望樓傳信。

// 請假審批,須要組長審批、經理審批、最後總監審批
class Action {
  constructor (name) {
    this.name = name;
    this.nextAction = null;
  }
  setNextAction (action) {
    this.nextAction = action;
  }
  handle () {
    console.log(`${this.name} 審批`)
    if (this.nextAction != null) {
      this.nextAction.handle();
    }
  }
}

let a1 = new Action('組長');
let a2 = new Action('經理');
let a3 = new Action('總監');
a1.setNextAction(a2);
a2.setNextAction(a3);
a1.handle();
複製代碼

命令模式 Command

概念:將請求與實現解耦並封裝成獨立對象,從而使不一樣的請求對客戶端的實現參數化。

也就是執行命令時,將發佈者和執行者分開,在中間加入命令對象,做爲中轉站。

應用實例:

命令模式的實例在生活中很常見,這裏咱們只舉一個例子,留給你們想象的空間。

咱們去飯店點菜(發佈者),無須知道廚師的名字(執行者),只需告訴服務員便可(中轉站)。

// 接受者 廚師
class Receiver {
  go () {
    console.log('接受者執行請求');
  }
}
// 命令對象 服務員
class Command {
  constructor (receiver) {
    this.receiver = receiver;
  }
  action () {
    console.log('命令對象->接受者->對應接口執行');
    this.recerver.go();
  }
}
// 發佈者 顧客吃飯點菜
class Invoker {
  constructor (command) {
    this.command = command;
  }
  invoke () {
    console.log('發佈者發佈請求');
    this.command.action();
  }
}

let cook = new Receiver();
let waiter = new Command(cook);
let Diner = new Invoker(waiter);
Diner.invoke();
複製代碼

訪問者模式 Visitor

概念:針對於對象結構中的元素,定義在不改變該對象的前提下訪問結構中元素的新方法

訪問者模式就是將數據操做和數據結構進行分離。

應用實例:

您在朋友家作客,您是訪問者,朋友接受您的訪問,您經過朋友的描述,而後對朋友的描述作出一個判斷,這就是訪問者模式。

使用訪問者模式,咱們可使對象擁有像數組同樣的pushpopsplice方法。

var Visitor = (function() {
      return {
        splice: function(){
          // splice方法參數,從原參數的第二個參數開始算起
          var args = Array.prototype.splice.call(arguments, 1);
          // 對第一個參數對象執行splice方法
          return Array.prototype.splice.apply(arguments[0], args);
        },
        push: function(){
          // 強化類數組對象,使他擁有length屬性
          var len = arguments[0].length || 0;
          // 添加的數據從原參數的第二個參數算起
          var args = this.splice(arguments, 1);
          // 校訂length屬性
          arguments[0].length = len + arguments.length - 1;
          // 對第一個參數對象執行push方法
          return Array.prototype.push.apply(arguments[0], args);
        },
        pop: function(){
          // 對第一個參數對象執行pop方法
          return Array.prototype.pop.apply(arguments[0]);
        }
      }
    })();
複製代碼

中介者模式 Mediator

概念:經過中介者對象封裝一系列對象之間的交互,使對象之間再也不相互引用,下降他們之間的耦合。

應用實例:

1.中國加入 WTO 以前是各個國家相互貿易,結構複雜,如今是各個國家經過 WTO 來互相貿易。

2.機場調度系統。

3.MVC 框架,其中C(控制器)就是 M(模型)和 V(視圖)的中介者。

class Mediator {
  constructor (a, b) {
    this.a = a;
    this.b = b;
  }
  setA () {
    let number = this.b.number;
    this.a.setNumber(number * 100);
  }
  setB () {
    let number = this.a.number;
    this.b.setNumber(number / 100);
  }
}

class A {
  constructor () {
    this.number = 0;
  }
  setNumber (num, m) {
    this.number = num;
    if (m) {
      m.setB();
    }
  }
}

class B {
  constructor () {
    this.number = 0;
  }
  setNumber (num, m) {
    this.number = num;
    if (m) {
      m.setA();
    }
  }
}
複製代碼

備忘錄模式 Memento

概念:在不破壞對象的封裝性的前提下,在對象以外捕獲並保存該對象內部的狀態以便往後對象使用或者對象恢復到之前的某個狀態。

應用實例:

分頁組件的時候,點擊下一頁獲取新的數據,可是當點擊上一頁時,又從新獲取數據,形成無謂的流量浪費,這時能夠對數據進行緩存。

// Memento 備忘錄對象 只供原發器使用 提供狀態提取方法
var Memento = function(state){
    var _state = state;
    this.getState = function(){
        return _state;
    }
}
// Originator 原發器
var Originator = function(){
    var _state;
    this.setState = function(state){
        _state = state;
    }
    this.getState = function(){
        return _state;
    }
    // saveStateToMemento 建立一個備忘錄 存儲當前狀態
    this.saveStateToMemento = function(){
        return new Memento(_state);
    }
    this.getStateFromMemento = function(memento){
        _state = memento.getState();
    }
}
// 負責人 負責保存備忘錄 可是不能對備忘錄內容進行操做或檢查
var CareTaker = function(){
    var _mementoList = [];
    this.add = function(memento){
        _mementoList.push(memento);
    }
    this.get = function(index){
        return _mementoList[index];
    }
}
 
var originator = new Originator();
var careTaker = new CareTaker();
originator.setState("State 1");
originator.setState("State 2");
careTaker.add(originator.saveStateToMemento());
originator.setState("State 3");
careTaker.add(originator.saveStateToMemento());
originator.setState("State 4");
 
console.log("當前狀態: " + originator.getState());
// 當前狀態: State 4
originator.getStateFromMemento(careTaker.get(0));
console.log("恢復第一次保存狀態: " + originator.getState());
// 恢復第一次保存狀態: State 2
originator.getStateFromMemento(careTaker.get(1));
console.log("恢復第二次保存: " + originator.getState());
// 恢復第二次保存: State 3
複製代碼

侷限性:備忘錄模式對資源的消耗過大。

迭代器模式 Iterator

概念:在不暴露對象內部結構的同時,能夠順序地訪問聚合對象內部的元素。

這種模式用於順序訪問集合對象的元素,不須要知道集合對象的底層表示。

應用實例:

絕大部分語言內置了迭代器。

1.JavaScript的Array.prototype.forEach

2.jQuery中$.each

$.each([1, 2, 3], function(i ,n){
  console.log('當前下標爲: '+ i,'當前值爲:' + n);
})
// 下標: 0 當前值:1 
// 下標: 1 當前值:2 
// 下標: 2 當前值:3
複製代碼

還有ES6中的Iterator

解釋器模式 Interpreter

概念:對於一種語言,給出其文法表示形式,並定義一種解釋器,經過使用這些解釋器來解釋語言中定義的句子。

這種模式實現了一個表達式接口,該接口解釋一個特定的上下文。

應用實例:

1.編譯器

2.正則表達式

參考:

《JavaScript設計模式》張容銘


交流

歡迎關注個人我的公衆號,文章將同步發送,後臺回覆【福利】便可免費領取海量學習資料。

你的前端食堂,記得按時吃飯。

相關文章
相關標籤/搜索