每天造輪子第七天 - 中間件實現 - 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

相關文章
相關標籤/搜索