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