摘要:但是,這樣做的后果就是,我們會(huì)不斷的改變本體,就像把鳳姐送去做整形手術(shù)一樣。在中,我們叫做引用裝飾。所以,這里引入的裝飾模式裝飾親切,熟悉,完美。實(shí)例講解裝飾上面那個(gè)例子,只能算是裝飾模式的一個(gè)不起眼的角落。
裝飾者,英文名叫decorator. 所謂的"裝飾",從字面可以很容易的理解出,就是給 土肥圓,化個(gè)妝,華麗的轉(zhuǎn)身為白富美,但本體還是土肥圓。
說(shuō)人話.
咳咳~
在js里面一切都是對(duì)象,而且函數(shù)就是一等對(duì)象。 在普通的Object中,我們可以很容易的添加屬性或者其他方法,當(dāng)然函數(shù)也不例外。 但是,這樣做的后果就是,我們會(huì)不斷的改變本體,就像把鳳姐送去做整形手術(shù)一樣。 當(dāng)然,結(jié)果有好有壞,也許鳳姐就是下一個(gè)angelababy,也許... 所以,為了我們代碼的純潔度(就算你是丑小鴨), 我們可以不動(dòng)刀子的情況下,讓你變得又白又美。
引用裝飾這個(gè)是裝飾的初級(jí)階段,就是抹點(diǎn)粉而已。 在js中,我們叫做引用裝飾。
talk is cheap, show u code
//我們給jimmy函數(shù)額外添加其他的功能 var jimmy = function(){ console.log("jimmy"); } var _jimmy = jimmy; jimmy = function(){ _jimmy(); console.log("I love HuaHua"); } jimmy();
這個(gè)的應(yīng)用場(chǎng)景主要就是在多人協(xié)作和框架設(shè)計(jì)里面。比如,李冰巖已經(jīng)使用了onload函數(shù),但是,小舟又想使用onload函數(shù)。 這樣會(huì)造成一個(gè)問題,如果小舟直接改動(dòng)的話,他需要看的是李冰巖寫的蜜汁代碼,而且還要防止不會(huì)引起錯(cuò)誤。這無(wú)疑是很困難的,所以在這里,可以使用引用裝飾,來(lái)給onload在添加一層。
//這是小李的蜜汁代碼 var xiaoLi = function(){ console.log("蜜汁代碼"); } window.onload = xiaoLi; //小李已經(jīng)綁定好onload函數(shù)了 //接下來(lái)小舟需要改動(dòng)onload代碼 var fn = window.onload; var xiaoZhou = function(){ fn(); conosle.log("整潔代碼"); } window.onload = function(){ //根據(jù)onload的特性,直接覆蓋掉小李的code xiaoZhou(); }
所以引用裝飾的用處還是蠻大的。
大你妹啊~~
啊。。。。
(另一Me) 我們來(lái)分析一下,上面那個(gè)引用模式有什么弊端(好處已經(jīng)都知道了).
首先,我們使用引用模式的時(shí)候,必定會(huì)添加一個(gè)多余的引用對(duì)象,比如上文的"fn".
而且隨著你程序鏈的增加,中間對(duì)象一定會(huì)和你節(jié)點(diǎn)同等數(shù)量的。當(dāng)然,起名我就不說(shuō)了,關(guān)鍵是,一大堆無(wú)用的代碼放在那里,感覺很不爽的。 所以,這里引入AOP的裝飾模式.
親切,熟悉,完美。 我們見過(guò)AOP應(yīng)該不止一次了,在職責(zé)鏈模式使用過(guò),在迭代器模式使用過(guò)等等。使用這么多次,好像還沒有對(duì)AOP做一些基本解釋呢?所以,這里給大家咻咻.
AOP中文名叫面向切面編程。 先說(shuō)一下這個(gè)名詞,“面向”這詞應(yīng)該不用解釋,關(guān)鍵"切面"是什么鬼。 如果大家做過(guò)sangwich,應(yīng)該就知道,首先我們買來(lái)一塊面包,需要將面包切開,然后在切面上面加上一些flavoring,比如蔬菜,火腿,培根之類的。 恩,對(duì)比js程序來(lái)說(shuō),一個(gè)程序鏈就相當(dāng)于你買回來(lái)的面包,flavoring就是你想加的功能函數(shù),如何將函數(shù)正確的放置在程序鏈中合適的位置,這就是AOP做的事情。
首先,再次將兩個(gè)動(dòng)態(tài)函數(shù)咻咻:
Function.prototype.after = function(fn){ var _this = this; return function(){ var res = _this.apply(this,arguments); fn.apply(this,arguments); return res; } } Function.prototype.before = function(fn){ var _this = this; return function(){ fn.apply(this,arguments); return _this.apply(this,arguments); } }
這兩個(gè)函數(shù)的組合構(gòu)成了js中AOP模式的精華.而AOP最常用的就是講與業(yè)務(wù)邏輯無(wú)關(guān)的功能動(dòng)態(tài)織入到主程序中。
talk is cheap , show u code
舉個(gè)栗子吧: 使用AOP計(jì)算程序運(yùn)行事件
//純手寫計(jì)算函數(shù)運(yùn)行事件 function factorial(n) { //最基本的階乘計(jì)算 if (n === 1) return 1; return n * factorial(n - 1); } function calTime(n){ var start = new Date().getMilliseconds(); factorial(n); console.log(new Date().getMilliseconds() - start+"ms"); } calTime(1000);
可以得出耗費(fèi)的時(shí)間,但是,如果還有其他的函數(shù)需要測(cè)試,那么這么做的意義并沒有很大的價(jià)值。我們使用AOP進(jìn)行重構(gòu)。
function factorial(n) { //最基本的階乘計(jì)算 if (n === 1) return 1; return n * factorial(n - 1); } var calTime = (function(){ var start; return function(){ if(!start){ //給開始時(shí)間賦值 start = new Date().getMilliseconds(); }else{ console.log(new Date().getMilliseconds()-start+"ms"); start = undefined; } } })(); var calcu = factorial.before(calTime).after(calTime)(200);
這樣很好的將計(jì)時(shí)功能從業(yè)務(wù)邏輯中提取出來(lái),而且看著真的很有angelababy的味道誒.
使用AOP的時(shí)候需要注意一點(diǎn)就是,before&after執(zhí)行完后,返回的結(jié)果都是第一個(gè)函數(shù)的內(nèi)容。
var result = function(){ return 1; }.before(function(){ return 2; }).after(function(){ return 3; }); console.log(result()); //1
我們大致的了解了AOP的用法和理論,可以針對(duì)于開頭所說(shuō)的例子進(jìn)行重構(gòu).
window.onload = function(){ console.log("小李的蜜汁代碼"); } var fn = window.onload; fn.before(function(){ console.log("整潔代碼"); }); window.onload = fn;
看起來(lái),比上面那個(gè)栗子清晰很多,而且使用before和after也十分利于代碼的閱讀。
實(shí)例講解AOP裝飾上面那個(gè)例子,只能算是AOP裝飾模式的一個(gè)不起眼的角落。 AOP引用的場(chǎng)景在js中,或者說(shuō)在任何一門語(yǔ)言中都是大放光彩的。 在js中,"細(xì)粒度"對(duì)象是程序中復(fù)用性最高的對(duì)象,能把對(duì)象用細(xì)粒度的形式表示,那么AOP無(wú)疑是最佳的選擇。
在寫業(yè)務(wù)邏輯的時(shí)候,我們最大的問題就是判斷邏輯,使用大量的if語(yǔ)句,而這都可以經(jīng)過(guò)思考巧妙化解。比如,我在寫購(gòu)買課程的時(shí)候就會(huì)遇到一些邏輯。 只有當(dāng)課程數(shù)目符合要求的時(shí)候,購(gòu)買的效果才能有效.
按正常的業(yè)務(wù)邏輯編寫
var buy = function(){ if(confirm()){ //驗(yàn)證購(gòu)買信息是否合法 http.buyCourse("xxx"); //發(fā)起請(qǐng)求 } } var confirm = function(){ console.log("驗(yàn)證購(gòu)買數(shù)量"); } document.querySelector(".buy").addEventListener("click",function(){ buy(); },false);
使用AOP裝飾模式重構(gòu)后
var buy = function(){ http.buyCourse("xxx"); //給后臺(tái)發(fā)起請(qǐng)求,驗(yàn)證 } var confirm = function(){ console.log("驗(yàn)證購(gòu)買數(shù)量"); } var buy = buy.before(confirm); document.querySelector(".buy").addEventListener("click",function(){ buy(); },false);
美美代碼的 滿滿即視感?。?!
不夠,老板,再來(lái)份糖炒栗子~
好嘞~
這里我們只是獲取函數(shù)的結(jié)果,那我們想直接干預(yù)傳遞的參數(shù),可以嗎?
當(dāng)然可以。
我們研究一下,before的內(nèi)部構(gòu)造(after是一樣的)
Function.prototype.before = function(fn){ var _this = this; return function(){ fn.apply(this,arguments); return _this.apply(this,arguments); } }
這里,由于arguments是引用類型,如果fn改變了arguments,則會(huì)反映到_this.apply的arguments也會(huì)發(fā)生改變。 而這個(gè)應(yīng)用場(chǎng)景就是,給ajax的地址添加上你需要的參數(shù)。
在實(shí)際項(xiàng)目中,一開始的接口,就是一個(gè)普普通通的地址,發(fā)請(qǐng)求,然后獲取參數(shù)。
http.ajax(url,type).then(...)
想這樣的使用,但是某天,你的leader提高了要求等級(jí),將地址后面都加上一個(gè)token參數(shù),或者說(shuō)一個(gè)口令的md5或sha1的計(jì)算值,我想,這尼瑪工作量應(yīng)該不小。
當(dāng)然,我們可以直接將url進(jìn)行傳遞。
var http = { ajax1(url){ url += param.getToken(); sendAjax(url); }, ajax2(url){ ... } ... }
而且,萬(wàn)一哪天你的leader說(shuō),哎,這樣做安全性還是不太高,要不加兩個(gè)token混淆一下。
啊~啊~啊~啊~(混淆你妹啊,過(guò)不過(guò)年啦)
如果你繼續(xù)這么寫,我相信,年終獎(jiǎng)是有的,但是,春運(yùn)火車票你估計(jì)是摸不著了。
所以可以使用AOP進(jìn)行動(dòng)態(tài)織入。要知道,參數(shù),我AOP也是可以動(dòng)的。
function dealUrl(url){ url+=param.getToken(); } http.ajax = http.ajax.before(dealUrl); http.ajax("www.example.com"); //此時(shí)的Url = www.example.com?token=23jkfd3kjfdksjfkjds
而且,這個(gè)處理url函數(shù),我也是可以扔到任意一個(gè)js文件里面復(fù)用的耶.
棒!!!
我AOP可以動(dòng)你要的參數(shù),而且,我還可以把我的結(jié)果給你是咻咻,如果我不讓你執(zhí)行,你永遠(yuǎn)也不會(huì)執(zhí)行,哈哈哈哈~~~~
對(duì)不起,,上面那段是我意淫AOP說(shuō)的。。。 其實(shí)AOP可以算是萬(wàn)能的配置工具,比如表單驗(yàn)證吧。 我們經(jīng)常會(huì)把表單驗(yàn)證和表單結(jié)果發(fā)送耦合在一起。
像這樣
var sendRes = function(){ if(user.userName === ""){ alert("用戶名不能為空~"); return; }else if(user.password === ""){ alert("密碼不能為空~"); return; } http.sendUser("xxx"); //驗(yàn)證成功發(fā)送用戶信息 }
一個(gè)函數(shù)里面同時(shí)含有了兩個(gè)職責(zé),一個(gè)驗(yàn)證一個(gè)發(fā)送用戶信息。 所以我們現(xiàn)在的主要目的就是解耦。
回想一下,以前表單驗(yàn)證我們使用策略模式,解耦了驗(yàn)證,這里我們?cè)俅问褂谩?/p>
var sendRes = function(){ var detect = new Detect(); //策略者模式 detect.add(user.userName,["notEmpty"]); detect.add(user.password,["notEmpty"]); var notPass = detect.getResult(); if(notPass){ //如果沒通過(guò) console.log(notPass); return; } http.sendUser("xxx"); //驗(yàn)證成功發(fā)送用戶信息 }
可以使用上面那個(gè)驗(yàn)證,但是結(jié)果是,驗(yàn)證和策略模式還是在一起。我們?cè)偈褂肁OP進(jìn)行解耦。首先我們得重構(gòu)一下before函數(shù)
Function.prototype.before = function(fn){ var _this = this; return function(){ var res = fn.apply(this,arguments); //值為Boolean,表示是否繼續(xù)向下傳遞 if(res==="next"){ //如果返回不成立,則繼續(xù)向下傳遞 return _this.apply(this,arguments); } return res; } }
看到這里,有些同學(xué)應(yīng)該明白是怎么一回事了。沒錯(cuò),就是根據(jù)before里面驗(yàn)證的結(jié)果判斷是否執(zhí)行下個(gè)發(fā)送請(qǐng)求的功能函數(shù)。
當(dāng)然,如果不想污染原型,你也可以自定義一個(gè)函數(shù)。
var before = function(beforeFn,fn){ return function(){ var res = beforeFn.apply(this,arguments); if(res==="next"){ return fn.apply(this,arguments); } } }
這樣寫也是可以的。
我們先按原型方式寫,這樣直觀一點(diǎn)
var sendRes = function(){ http.sendUser("xxx"); //驗(yàn)證成功發(fā)送用戶信息 } sendRes = sendRes.before(function(){ var detect = new Detect(); //策略者模式 detect.add(user.userName,["notEmpty"]); detect.add(user.password,["notEmpty"]); var notPass = detect.getResult(); if(notPass){ //如果沒通過(guò) console.log(notPass); } return "next"; });
可以看出,驗(yàn)證那部分已經(jīng)完全和發(fā)送用戶信息的功能函數(shù)完全給解耦了。 這樣不僅提高了函數(shù)的重用性,而且也讓你的代碼朝著“細(xì)粒度”方向大步前進(jìn).
辯證裝飾者模式其實(shí),裝飾者模式和職責(zé)鏈模式的形式是完全一樣的,所以,他們的弊端也是類似的。鏈造的過(guò)長(zhǎng),對(duì)于性能來(lái)說(shuō)就是一次rape.所以,還是那句話,不要為了模式而模式,沒有萬(wàn)能的模式。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/78552.html
摘要:下裝飾者的實(shí)現(xiàn)了解了裝飾者模式和的概念之后,我們寫一段能夠兼容的代碼來(lái)實(shí)現(xiàn)裝飾者模式原函數(shù)拍照片定義函數(shù)裝飾函數(shù)加濾鏡用裝飾函數(shù)裝飾原函數(shù)這樣我們就實(shí)現(xiàn)了抽離拍照與濾鏡邏輯,如果以后需要自動(dòng)上傳功能,也可以通過(guò)函數(shù)來(lái)添加。 showImg(https://segmentfault.com/img/bVbueyz?w=852&h=356); 什么是裝飾者模式 當(dāng)我們拍了一張照片準(zhǔn)備發(fā)朋友...
摘要:裝飾者模式定義裝飾者模式能夠在不改變對(duì)象自身的基礎(chǔ)上,在程序運(yùn)行期間給對(duì)像動(dòng)態(tài)的添加職責(zé)。與繼承相比,裝飾者是一種更輕便靈活的做法。 裝飾者模式 定義 : 裝飾者(decorator)模式能夠在不改變對(duì)象自身的基礎(chǔ)上,在程序運(yùn)行期間給對(duì)像動(dòng)態(tài)的添加職責(zé)。與繼承相比,裝飾者是一種更輕便靈活的做法。 在不改變對(duì)象自身的基礎(chǔ)上,在程序運(yùn)行期間給對(duì)象動(dòng)態(tài)地添加一些額外職責(zé) 特點(diǎn) : 可以動(dòng)態(tài)的...
摘要:用戶名不能為空密碼不能為空校驗(yàn)未通過(guò)使用優(yōu)化代碼返回的情況直接,不再執(zhí)行后面的原函數(shù)用戶名不能為空密碼不能為空 本文是《JavaScript設(shè)計(jì)模式與開發(fā)實(shí)踐》的學(xué)習(xí)筆記,例子來(lái)源于書中,對(duì)于設(shè)計(jì)模式的看法,推薦看看本書作者的建議。 什么是裝飾者模式? 給對(duì)象動(dòng)態(tài)增加職責(zé)的方式成為裝飾者模式。 裝飾者模式能夠在不改變對(duì)象自身的基礎(chǔ)上,在運(yùn)行程序期間給對(duì)象動(dòng)態(tài)地添加職責(zé)。這是一種輕便靈活...
摘要:會(huì)一直完善下去,歡迎建議和指導(dǎo),同時(shí)也歡迎中用到了那些設(shè)計(jì)模式中用到了那些設(shè)計(jì)模式這兩個(gè)問題,在面試中比較常見。工廠設(shè)計(jì)模式使用工廠模式可以通過(guò)或創(chuàng)建對(duì)象。 我自己總結(jié)的Java學(xué)習(xí)的系統(tǒng)知識(shí)點(diǎn)以及面試問題,已經(jīng)開源,目前已經(jīng) 41k+ Star。會(huì)一直完善下去,歡迎建議和指導(dǎo),同時(shí)也歡迎Star: https://github.com/Snailclimb... JDK 中用到了那...
摘要:修飾者模式設(shè)計(jì)模式中的修飾者模式能動(dòng)態(tài)地給目標(biāo)對(duì)象增加額外的職責(zé)。修飾者模式調(diào)用的時(shí)序圖如下圖所示。的實(shí)現(xiàn)原理和修飾者模式類似。 ?在上邊一篇文章中我們介紹了Spring AOP的基本概念,今天我們就來(lái)學(xué)習(xí)一下與AOP實(shí)現(xiàn)相關(guān)的修飾者模式和Java Proxy相關(guān)的原理,為之后源碼分析打下基礎(chǔ)。 修飾者模式 ?Java設(shè)計(jì)模式中的修飾者模式能動(dòng)態(tài)地給目標(biāo)對(duì)象增加額外的職責(zé)(Respon...
閱讀 3012·2021-11-24 10:22
閱讀 3058·2021-11-23 10:10
閱讀 1367·2021-09-28 09:35
閱讀 1761·2019-08-29 13:16
閱讀 1400·2019-08-26 13:29
閱讀 2798·2019-08-26 10:27
閱讀 687·2019-08-26 10:09
閱讀 1450·2019-08-23 18:05