設計模式之“中間件模式”


實際場景

在日常的開發過程中,我們在編寫業務代碼時候,無法避免有些業務邏輯復雜而導致業務代碼寫得又長又亂。有些邏輯像一個過程,在不同的節點需要做不同的操作。

比如,我們在開發的過程中經常會遇到數據提交這樣一個場景。我們的目的是數據提交,但是在提交之前,我們需要對數據進行驗證,驗證正確之后,對數據發送進行上報,上報之后才是我們的目標操作提交數據。提交數據之后我們還需要跳轉到提交成功的頁面。這時候我們一般的做法會是這樣:

if(//驗證數據){

  //上報數據操作

  //提交數據操作

  //跳轉成功頁面

}

這時候我們會將整個流程的代碼糅合在一起,如果代碼簡單一點還好,但是如果每一個步驟都有大量的邏輯操作,估計會讓人抓狂。

解決方案

對於這一類的流程事件,我們可以采用分解這些事件,當需要用到這些事件操作時,我們將操作插入到核心事件完成所需要的不同步驟中。我們通過下面的方式來實現提交的功能:

Function.prototype.before = function(fn){
    var self = this;
    return function(){
        var res = fn.call(this);
        if(res){
            self.call(this,arguments);
        }
    }
};

Function.prototype.after = function(fn){
    var self = this;
    return function(){
        self.call(this,arguments);
        fn.call(this);
    }
};

function report(){
    console.log('上報數據');
    return true;
}

function validate(){
    console.log('驗證數據');
    if( + new Date()%2 == 0){
        return true;
    }else{
        return false;
    }    
}

function submit(){
    console.log('提交數據');
}

function goback(){
    console.log('返回首頁');
}

submit.before(report).before(validate).after(goback)();

通過上面的代碼,我們將各個階段的業務給分解開來,這樣做的好處很明顯,我們只要關注各個階段的代碼實現,最后將各個階段通過管道式的方式拼裝起來。有利於我們代碼邏輯的解耦符合我們高內聚低耦合的原則。同時,各部分的代碼又獨立存在,當其他業務邏輯需要用到的時候,我們只需要把需要的部分取出來,拼裝在需要的邏輯上面就可以了。這又有利於代碼的復用。

但是,上面的代碼又有兩個問題

1、一串長長的鏈式調用,不方便維護者理解

2、如何before或者after的參數是一個異步操作的話,又需要做一些patch

有沒有其他的方法來實現既能隔離業務,又能方便地使用呢。我們來看express的實現方式

Express中間件的實現

我們來看express的實現方法

 

var express = require('express');
var app = express();
 
app.use(function(req, res, next) {
  console.log('數據統計');
  next();//執行權利傳遞給
});

app.use(function(req, res, next) {
  console.log('日志統計');
  next();
});

app.get('/', function(req, res, next) {
  res.send('Hello World!');
});

app.listen(3000);
//整個請求處理過程就是先數據統計、日志統計,最后返回一個Hello World!

 

上圖的運作流程

從上圖來看,每一個管道都是一個中間件,每個中間件通過next方法傳遞執行權給下一個中間件,express就是一個收集並調用各種中間件的容器。

中間件就是一個函數,通過expressuse方法接收中間件,每個中間件有express傳入的reqresnext參數。如果要把請求傳遞給下一個中間件必須使用 next() 方法。當調用res.send方法則此次請求結束,node直接返回請求給客戶,但是若在res.send方法之后調用next方法,整個中間件鏈式調用還會往下執行,因為當前hello world所處的函數也是一塊中間件,而res.send只是一個方法用於返回請求。

借用中間件實現

我們可以借用中間件思想來分解我們的前端業務邏輯,通過next方法層層傳遞給下一個業務。

代碼如下:

var MidWare = function(){
    this.cache = [];
    this.options = {}
}

MidWare.prototype.use = function(fn){
    if(typeof fn !== 'function'){
        console.log('need a function');
        return false;
    }
    this.cache.push(fn);
    return this;
}

MidWare.prototype.next = function(argument){
    if(this.midwares && this.midwares.length > 0){
        var ware = this.midwares.shift();
        ware.call(this,this.options || {}, this.next.bind(this))
    }
};

MidWare.prototype.handleRequest = function(options){
  this.midwares = this.cache.map(function(fn){
    return fn;
  });
  this.options = options;//緩存數據
  this.next();
}

var submitForm = new MidWare();

//驗證
submitForm.use(function(options, next){
    console.log('驗證數據');
    next();
})

//上報
submitForm.use(function(options, next){
    setTimeout(function(){
        console.log('上報數據');
        next();
    }, 3000)
    
})

//提交數據
submitForm.use(function(options, next){
    console.log('提交數據');
    next();
})

//返回首頁
submitForm.use(function(options, next){
    console.log('返回首頁');
})

submitForm.handleRequest();

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM