zoukankan      html  css  js  c++  java
  • 设计模式之“中间件模式”

    实际场景

    在日常的开发过程中,我们在编写业务代码时候,无法避免有些业务逻辑复杂而导致业务代码写得又长又乱。有些逻辑像一个过程,在不同的节点需要做不同的操作。

    比如,我们在开发的过程中经常会遇到数据提交这样一个场景。我们的目的是数据提交,但是在提交之前,我们需要对数据进行验证,验证正确之后,对数据发送进行上报,上报之后才是我们的目标操作提交数据。提交数据之后我们还需要跳转到提交成功的页面。这时候我们一般的做法会是这样:

    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();
  • 相关阅读:
    递归算法详解
    树、森林和二叉树的转换
    C++ 类的静态成员详细讲解
    C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区
    C++中的static关键字的总结
    C/C++中static关键字详解
    配置文件
    Spring Boot 注释
    使用 Spring Boot 快速构建 Spring 框架应用
    Myeclipse快捷键(二)
  • 原文地址:https://www.cnblogs.com/caizhenbo/p/6825223.html
Copyright © 2011-2022 走看看