每天造轮子第七天 - 中间件实现 - Compose 的 N 种姿式

造轮子计划

计划每天变git

1、中间件是什么

中间件能够有两个不一样的解释。github

1. 传统的中间件概念

维基百科算法

中间件(英语:Middleware),是提供系统软件和应用软件之间链接的软件,以便于软件各部件之间的沟通,特别是应用软件对于系统软件的集中的逻辑,在现代信息技术应用框架如Web服务、面向服务的体系结构等中应用比较普遍。如数据库、Apache的Tomcat,IBM公司的WebSphere,BEA公司的WebLogic应用服务器,东方通公司的Tong系列中间件,以及Kingdee公司的等都属于中间件。数据库

在好久好久之前,express

1988年5月,求伯君的普通技术人员在一个宾馆的出租房间里凭借一台386电脑写出了WPS(Word Processing System)1.0,今后开创了中文字处理时代。编程

那个时代写程序基本就是用C从在操做系统上直接开发redux

操做系统 => 业务程序设计模式

可是后来你会发现一个复杂的业务程序一般状况下能够分化为两层,能够抽离出一层程序和业务彻底无关好比Web服务,数据存储等。缓存

操做系统 => 中间件(Middleware) => 业务程序安全

这个就是中间件最原始的概念。

中间件能够屏蔽操做系统底层,减小程序设计的复杂性。

操做系统(Linux) => 中间件(Middleware) => 业务程序

操做系统(Windows) => 中间件(Middleware) => 业务程序

操做系统(Unix SCO) => 中间件(Middleware) => 业务程序

常见的中间件产品

  • 应用服务器 : JBoss、Tomcat、Geronimo、JOnAS
  • 消息中间件: Kafka、 RabbitMQ
  • 交易中间件: TUXEDO、TUXONE

2. JS世界中的中间件

JS世界中不管是Express、Koa、Redux中都会有中间件实现。其实这个中间件主要是为了方便的将业务逻辑拼装成一个逻辑处理链条。实际上是对设计模式中责任链模式的一种实现。咱们一般也会将他称为洋葱圈模型。

image-20201109113922290

3. 为切面编程而生的

对于复杂的系统,切面编程是刚需。完成一个核心逻辑以前和以后,仍是粗了错误都须要作对应的处理。

image-20201109120109356

好比转帐:

  • 转帐前 : 鉴权 安全性 开启事务

  • 转帐后: 操做日志 关闭事务

  • 异常: 帐户回滚 错误日志

想象一下假设咱们hard coding这个代码有多麻烦。

transfer() {
  // 鉴权
  tokenCheck()

  // 开启事务
  tx.begin()
  try {

    // 核心逻辑

    // 操做日志
    log(' xxxxxx ')
  } cache(err) {
    // 错误日志
    log(' xxxxxx ')
    // 回滚事务
    tx.rollback
  } finally {
    // 关闭事务
    tx.end
  }
}

复制代码

其实 尚未完 ,若是你在程序中还要加上同步锁、资源池获取(好比数据库链接池)、缓存呢。

image-20201109182530443

解决办法就是须要提供AOP编程的可能。

“面向切面编程”,这样的名字并非很是容易理解,且容易产生一些误导。有些人认为“OOP/OOD11即将落伍,AOP是新一代软件开发方式”。显然,发言者并无理解AOP的含义。Aspect,的确是“方面”的意思。不过,汉语传统语义中的“方面”,大多数状况下指的是一件事情的不一样维度、或者说不一样角度上的特性,好比咱们常说:“这件事情要从几个方面来看待”,每每意思是:须要从不一样的角度来看待同一个事物。这里的“方面”,指的是事物的外在特性在不一样观察角度下的体现。而在AOP中,Aspect的含义,可能更多的理解为“切面”比较合适。

若是有切面编程,代码一般只须要在一次性在定义在全部业务前都作什么就行了

@Before(tokenCheck) // 鉴权
@Before(tx.begin) // 开启事务
@After(log) // 操做日志
@After(tx.end) // 关闭事务
@Exception(log) //错误日志
@Exception(tx.rollback) // 回滚事务
traansferBase() {
  // 业务逻辑
}
复制代码

固然我这个是用Anotation的形式写的,总之就是只须要一次性定义,就能够所有执行。

满满的高内聚低耦合。

4. 洋葱圈的产生

其实为了达到AOP的目的有不少种实现方法,咱们所说的洋葱圈就是一种。

其实就是责任链模式,责任链模式至关于给核心任务加上了一次层层的洋葱圈。

中间件

至关于将事前处理,过后处理,和业务程序编成了链条。

中间件2

这样就能够达到切面编程的目的了。

2、需求

洋葱圈的实现须要考虑同步和异步状况。这里仅以同步状况为例。

详细的请参考代码 :github.com/su37josephx…

it('同步函数', async () => {
        const mockFn = jest.fn()
        // const mockFn = console.log
        const middlewares = [
            next => {
                mockFn('1 start')
                next()
                mockFn('1 end')
            },
            next => {
                mockFn('2 start')
                next()
                mockFn('2 end')
            }
        ]
        const func = compose(middlewares)

        viewLog && console.log('同步函数 > compose定义', compose.toString());

        func();
        const calls = mockFn.mock.calls
        viewLog && console.log('第一次', calls);
        expect(calls.length).toBe(4);
        expect(calls[0][0]).toBe('1 start');
        expect(calls[1][0]).toBe('2 start');
        expect(calls[2][0]).toBe('2 end');
        expect(calls[3][0]).toBe('1 end');

    })
复制代码

3、功能实现

其实洋葱圈要是写能够写个十几种。如下代码都是个人学生的做业。

No1:express 递归实现

No2:Koa 递归实现

No3:Koa Reduce实现

No4:Koa Class实现

No5:Redux Reduce实现

No6:Redux ReduceRight实现

No7:Redux ReduceRight Promise实现

No8:Chain of Responsibility Pattern 责任链模式实现

No9:[List 经过列表递归实现](

咱们就随便说几个,还有不少,欢迎你们PR 。还有哪些我后面会说。

1. Express递归实现

module.exports.compose = (middlewares = []) => {
    if (!Array.isArray(middlewares)) {
        middlewares = Array.from(arguments);
    }

    if (middlewares.some(fn => typeof fn !== 'function')) {
        throw new TypeError('Middleware must be composed of functions!');
    }

    return async () => {
        let idx = 0;
        async function next() {
            if (idx === middlewares.length) {
                return Promise.resolve();
            }
            if (idx < middlewares.length) {
                return Promise.resolve(middlewares[idx++](next));
            }
        }
        return await next();
    };
};

复制代码

2.Koa递归实现

module.exports.compose = (middlewares = []) => {
    if (!Array.isArray(middlewares)) {
        middlewares = Array.from(arguments);
    }

    if (middlewares.some(fn => typeof fn !== 'function')) {
        throw new TypeError('Middleware must be composed of functions!');
    }

    return function () {
        return dispatch(0);
        function dispatch(i) {
            let fn = middlewares[i];
            if (!fn) {
                return Promise.resolve();
            }
            return Promise.resolve(
                fn(function next() {
                    return dispatch(i + 1);
                })
            );
        }
    };
};

复制代码

3. Reduce实现

module.exports.compose = (middlewares = []) => {
    if (!Array.isArray(middlewares)) {
        middlewares = Array.from(arguments);
    }

    if (middlewares.length === 0) {
        return arg => arg;
    }

    if (middlewares.some(fn => typeof fn !== 'function')) {
        throw new TypeError('Middleware must be composed of functions!');
    }

    return (next = async () => {}) => middlewares.reduce((a, b) => arg => a(() => b(arg)))(next);
};

复制代码

image-20201110113646707

4、升华扩展

其实 就是责任链的实现,欢迎你们PR写完了保证你进入另一个变成世界

blog.csdn.net/liuwenzhe20…

请参考这篇大哥雄文

  • OOP风格

概括一下

  • 面向对象风格OOP
    • 解法一 用模板方法模式实现责任链模式
    • 解法二 用策略模式实现责任链模式
  • 函数式风格Functional Programming
    • 解法三 用一等公民函数替换策略模式实现责任链模式
    • 解法四 用偏应用函数实现责任链模式
    • 解法五 用偏函数实现责任链模式
  • 响应式风格Reactive Programming
    • 解法六 用Actor模型实现责任链模式
    • 解法七 用RXReactive eXtension响应式扩展实现责任链模式

欢迎各位大哥投稿 PR

相关文章
相关标签/搜索