Q: 什么是食物?
A: 食物通常以碳水化合物腊瑟、脂肪、蛋白質或水構成棒呛,能夠借由進食或是飲用為人類或者生物提供營養(yǎng)或愉悅的物質澈圈。
食物的來源可以是植物、動物或者其他界的生物巴帮,例如真菌溯泣,亦或發(fā)酵產(chǎn)品像是酒精。
生物攝取食物后晰韵,被生物的細胞同化,提供能量熟妓,維持生命及刺激成長雪猪。
前段時間討論到Java中的回調(diào)函數(shù)時,提到了面向切面(Aspect Oriented Programming)編程起愈。它是一種程序設計思維只恨,常常與面向對象(Object Oriented Programming)編程相比較,但是也常常被單獨拎出來討論抬虽。
OOP的誕生源自于人類與生俱來的分類抽象能力官觅,回想一下,讀小學的你阐污,是否就能輕而易舉地分辨“零食”和“正餐”的區(qū)別呢休涤?零食美味而充滿誘惑,但是對于你來說昂貴笛辟,而且吃太多存在挨罵的風險功氨,相對的,正餐則略顯的無聊和平淡手幢,但是在你餓了的時候往往能讓你的肚子不會咕咕叫捷凄。
于是自然而然你就可以將它們的共同點抽象出來,比如它們可以被食用围来,但是它們的味道跺涤、價格匈睁、食用時間乃至“副作用”之類的都不太一樣。它們中相同的屬性桶错,就可以分配給一個只對這些相同的屬性敏感的父元素航唆。而不同的部分,就單獨分配給父元素下屬的不同的子元素牛曹。這樣一來佛点,子元素就不光含有自己的屬性,也擁有了父元素的所有屬性黎比。
所以現(xiàn)在名詞解釋對你來說超营,會不會變得更容易呢?
面向切面
現(xiàn)在你知道了阅虫,事物中對于某些相同屬性的抽象演闭,構成了面向對象的基石。
來寫一個簡單的OOP的例子
// JavaScript ES6
class Person {
constructor(name) {
this.name = name;
}
greeting() {
console.log(`hello, my name is ${this.name}, `);
}
}
class Engineer extends Person {
constructor(name, level) {
super(name);
this.level = level;
}
// override
greeting() {
super.greeting();
console.log(`a ${this.level} Engineer.`);
}
}
class Architect extends Engineer {
constructor(name, level) {
super(name, level);
}
design(work) {
console.log(`right now, i\`m ${work}.`);
}
playGames(game, platform) {
console.log(`${this.name} is playing ${game} on ${platform} now.`);
}
}
let ted = new Architect('Ted', 'Junior');
ted.greeting();
ted.design('painting some blueprints');
>>>
hello, my name is Ted,
a Junior Engineer.
right now, i'm painting some blueprints.
侵入式切面
假設我們想要在對象 Person
的所有方法執(zhí)行前颓帝,加入一段邏輯米碰。我們有什么好辦法呢?
代理函數(shù)是個很好的選擇购城,我們在進行具體的業(yè)務調(diào)用時吕座,直接調(diào)用一個代理函數(shù)就行了。
function before(person, fn, ...args) {
console.log('something running before');
return fn.apply(person, args);
}
函數(shù) before
瘪板,將我們要調(diào)用的函數(shù)包裹起來吴趴,然后我們只需要這樣調(diào)用這個代理函數(shù),就能在其中的某個部分中添加邏輯侮攀。
before(ted, ted.design, 'painting some blueprints');
上面的全局函數(shù)锣枝,還可以直接添加進對象中,防止被不必要的類或者方法使用兰英。
Person.prototype.before = function (fn, ...args) {
console.log('do sthing before');
return fn.call(this, args);
};
ted.before(ted.design, 'painting some blueprints');
雖然這種形式大體上能解決我們的需求撇叁,但是這種寫法,仍然破壞了對象原有的調(diào)用形式畦贸。
// 原來的調(diào)用形式
ted.greeting();
ted.playingGames('DotA2', 'PC');
ted.design('painting some blueprints');
// 代理函數(shù)的調(diào)用形式
ted.before(ted.greeting);
ted.before(ted.playingGames, 'DotA2', 'PC');
ted.before(ted.design, 'painting some blueprints');
清一色的 before
函數(shù)陨闹,不僅寫起來頭暈,而且當你要改動的時候薄坏,所有調(diào)用這個代理函數(shù)的地方都要進行改動正林。這并不是我們想要的。
非侵入式切面
代理函數(shù)幫我們執(zhí)行了函數(shù)颤殴,雖然能按照我們的期望執(zhí)行程序觅廓,但我們更希望它直接返回函數(shù)給我們,這樣我們就無須費勁心思地去到處修改舊代碼涵但,同時往代碼中添加了新的功能杈绸。
function before(originTarget, fn) {
return function (...args) {
console.log('do sthing before');
return fn.apply(originTarget, args);
};
}
ted.playGames = before(ted, ted.playGames);
// 正常調(diào)用
ted.playGames('DotA2', 'PC');
以上代碼還可以寫成更加通用的寫法
function before (clazz, fn) {
return function (...args) {
console.log(`now time: ${new Date()}`);
return clazz.prototype[fn].apply(this, args);
}
}
ted.greeting = before(Person, 'greeting');
// 正常調(diào)用
ted.greeting();
程序中那些無法通過父元素所聚合的共同屬性帖蔓,但是又切實地影響到了程序的寫作——這種時候砰粹,切面編程就能發(fā)揮出它的用處攒磨。它將業(yè)務中的相同的部分抽象出來,組成一個個可拆卸的業(yè)務組件形庭。面向對象通過繼承和多態(tài)的縱軸讓代碼松耦合劫侧,而面向切面則是在業(yè)務并行展開的水平線上讓代碼松耦合埋酬。
中間件
提起AOP,總免不了讓人想到Java Spring框架中的監(jiān)聽器烧栋、攔截器写妥、過濾器。它們是典型的AOP編程思想的結晶——一條正常的程序流审姓,被幾個中間件攔截檢查珍特。
這啟發(fā)了我們,在程序設計上的一條新思路:程序暴露出一段執(zhí)行流魔吐,并且給開發(fā)者一把剪刀扎筒。它們可以隨意在允許的范圍內(nèi)剪開程序,并織入想要執(zhí)行的內(nèi)容酬姆。最后這一段執(zhí)行流合重新合并起來嗜桌,收入程序的深處。
簡單模擬一個中間件設計
// 切入點函數(shù)隊列
let fns = [];
// 切點計數(shù)
let fnCounter = 0;
// 啟動函數(shù)
function main() {
next();
}
// 執(zhí)行下一個函數(shù)
function next() {
let fn = fns[fnCounter++]; // 取出函數(shù)數(shù)組里的下一個函數(shù)
if (!fn) { // 如果函數(shù)不存在,return
return;
}
fn(next); // 否則,執(zhí)行下一個函數(shù)
}
// 將自定義的函數(shù)推入函數(shù)隊列
function processer(fn) {
fns.push(fn);
}
function loginCheck(next) {
if (...)
next();
else
return;
}
function characterFilter(next) {
// doing some character filtering
next();
}
function mainService() {
// 主業(yè)務
console.log('some main services');
}
processer(characterFilter);
processer(loginCheck);
processer(mainService);
main(); // 模擬程序啟動
原文地址: https://code.evink.me/2018/07/post/Aspect-Oriented-Programming/