前端解读面向切面编程(AOP)
前言
面向對象(OOP)作為經典的設計范式,對于我們來說可謂無人不知,還記得我們入行起始時那句經典的總結嗎-萬事萬物皆對象。
是的,基于OOP思想封裝、繼承、多態的特點,我們會自然而然的遵循模塊化、組件化的思維來設計開發應用,以到達易維護、可擴展、高復用的目的。
既然OOP這么多優點,那么經常被大家提起的面向切面編程(AOP)是什么回事呢,下面我們就一起來看一下。
AOP定義
第一步還是要知道aop是什么,先個來自維基百科的解釋:
面向側面的程序設計(aspect-oriented programming,AOP,又譯作面向方面的程序設計、觀點導向編程、剖面導向程序設計)是計算機科學中的一個術語,指一種程序設計范型。
側面的概念源于對面向對象的程序設計的改進,但并不只限于此,它還可以用來改進傳統的函數。
其從主關注點中分離出橫切關注點是面向側面的程序設計的核心概念。分離關注點使得解決特定領域問題的代碼從業務邏輯中獨立出來.
業務邏輯的代碼中不再含有針對特定領域問題代碼的調用,業務邏輯同特定領域問題的關系通過側面來封裝、維護. 這樣原本分散在在整個應用程序中的變動就可以很好的管理起來。
tip
確實有點那么不太清晰,有點亂。不過在亂之前,我們可以選能理解的部分先看一下:
- 側面(也就是切面) 用來描述分散在對象、類或函數中的橫切關注點。
重點在這,分散在對象中的橫切關注點,可以猜一下是什么,應該就是不同對象之間公用的部分 - 側面的概念源于對面向對象的程序設計的改進,它還可以用來改進傳統的函數. AOP 顯然不是OOP的替代品,是OOP的一種補充。
- 從主關注點中分離出橫切關注點是面向側面的程序設計的核心概念。
具體到業務項目中來說,主關注點就是業務邏輯了。針對特定領域問題代碼的調用,就是AOP要關注的部分
簡而言之,AOP是針對業務處理過程中的切面(即非業務邏輯部分,例如錯誤處理,埋點,日志等)進行提取.
它所面對的是處理過程中的某個步驟或階段,以獲得邏輯過程中各部分之間低耦合性的隔離效果(目的是降低耦合)。
具體到實現來說就是通過動態的方式將非主關注點部分插入到主關注點(一般是業務邏輯中)
說了這么多,可能不太明白,還是一起看代碼吧。
埋點場景
很普遍的這么個場景,需要點擊按鈕之后進行信息上報。 假設我們有這么個logger的工具,可以進行上報:
const logger = console.log //引入即可使用 logger('按鈕被點擊了')那么,我們直接擼起來吧:
const doSomething = ()=>{console.log('doSomething') } let clickHandler = ()=>{logger('doSomething之前')// n行代碼 doSomething() logger('doSomething之后')//n 行代碼 }看起來也沒什么的,簡單粗暴。
如果有30個按鈕,每個業務邏輯不同,都需要埋這個點(假設打點信息一致)。
我們30個函數里面,都要手動寫這個方法的話,這也太坑爹了吧。
主要是與業務代碼嚴重耦合,哪天不小心動了點其他內容,手抖誤刪了,就gg了。
后續維護的時候,簡直噩夢。
仔細看一下,這不就是符合AOP的使用前提嗎,那么試試AOP吧。
關注點劃分
根據前面提的,可以劃分下關注點。
| 業務邏輯(doSomething) | 埋點信息 logger |
前面提到AOP關注的是步驟具體到例子來說其實就是插入logger的步驟。
插入時機無非時業務邏輯執行之前或者之后的階段。
具體實現起來也不那么困難
實現思路
具體到js來說,由于語言本身的特性,天生就具有運行時動態插入邏輯的能力。
重點在于在原函數上增加其他功能并不改變函數本身。
畢竟函數可以接受一切形式的參數,當然函數也不例外了。
當傳入一個函數的時候,我們要對其操作的余地就很大了,
保存原函數,然后利用后續參數加上call或apply,就可以達到我們的目的。
此外為了給函數都增加一個屬性,我們在原型上操作就行了。
經典before或者after的實現
網上太多類似實現了,直接看代碼好了:
// action 即為我們的側關注點,即logger Function.prototype.after = function (action) {//保留當前函數,這里this指向運行函數即clickHandlervar func = this;// return 被包裝過的函數,這里就可以執行其他功能了。// 并且該方法掛在Function.prototype上,// 被返回的函數依然具有after屬性,可以鏈式調用return function () {// 原函數執行,這里不考慮異步var result = func.apply(this, arguments);// 執行之后的操作action.apply(this,arguments);// 將執行結果返回return result;}; }; // before 實現類似,只不過執行順序差別而已 Function.prototype.before = function (action) {var func = this;return function () {action.apply(this,arguments);return func.apply(this, arguments);}; };那么我們使用AOP改造之后的代碼就如下了:
const doSomething = ()=>{console.log('doSomething') } let clickHandler = ()=>{// n行代碼 doSomething() //n 行代碼 } clickHandler = clickHandler.before(()=>{logger('doSomething之前') }).after(()=>{logger('doSomething之后') }) clickHandler() // 執行結果和預期一致到這里就實現了面向切面編程,我們的業務邏輯里面只管業務本身,側關注點通過這種方式來動態引入,與主邏輯解耦,更加純凈、易于維護。
結束語
到這里,簡單的AOP就介紹完成了。利用這種模式結合我們js本身的特性,可以嘗試更多的可能。 例如我們react中常見的HOC、es7的裝飾者模式、HOF等,很多時候不得不感嘆大牛們思想的精髓,會讓我們有種頓悟的感覺。本文拋磚引玉,共同學習啦,對自己是總結和提高,更希望能幫助到需要的小伙伴。更多文章請移步我的博客
參考文章
AllyTeam - 用AOP改善javascript代碼
深入淺出 Javascript Decorators 和 AOP 編程
總結
以上是生活随笔為你收集整理的前端解读面向切面编程(AOP)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vue移动端项目缓存问题实践
- 下一篇: 前端解读控制反转(IOC)