其實生命就是這樣呻畸,從無到有移盆,從稚嫩到成熟。現狀態(tài)轉變自前一個狀態(tài)伤为,只是多了點東西味滞,它們就變得相互獨立,形同陌路钮呀。
裝飾者模式剑鞍,聽名字就會明白,是在原有對象的基礎上爽醋,給其裝飾一些東西蚁署,使其成為一個新的對象。舉個例子:我現在是一個單身狗蚂四,給我一個女朋友光戈,我就變成了有女朋友的單身狗;換句話說就是遂赠,我現在是一個單身狗對象久妆,給我裝飾了一個女朋友,我就成為一個有女朋友的單身狗對象跷睦。接下來用代碼闡述一下:
const SingleDog = function () {
}
SingleDog.prototype.say = function () {
console.log('我是一只單身狗');
}
const DecorateGfriend = function (men) {
this.men = men;
}
DecorateGfriend.prototype.say = function () {
this.men.say();
console.log('我有女朋友');
}
const me = new DecorateGfriend(new SingleDog());
me.say(); // 我是一只單身狗筷弦,我有女朋友
OK,我們先來看一下上面的代碼抑诸,至關重要的DecorateGfriend做了二件事情:1. 調用了SingleDog的say函數星掰;2. 額外打印出了一句話橘蜜。這兩點其實就是裝飾者模式的精髓:在保持原有對象功能的基礎上,增加新功能
上面的代碼看起來不太好看组题,又多又亂而且DecorateGfriend似乎違反了開放-封閉原則维蒙,即我要是想秀波恩愛陪踩,額外打印出一句“我很幸附缧”之類云云萝招,需要直接改DecorateGfriend的代碼,這樣子是不行的辛块,所以有沒有什么好的方法嘞畔派?下面給出 2 個經典的AOP裝飾函數:
Function.prototype.before = function(beforefn) {
const self = this;
return function () {
beforefn.apply(this, arguments);
return self.apply(this, arguments);
}
}
Function.prototype.after = function(afterfn) {
const self = this;
return function () {
const ret = self.apply(this, arguments);
afterfn.apply(this, arguments);
return ret;
}
}
來看看第一個函數,正如上面提到的憨降,做了兩件事情:1. 先調用了傳遞進來的函數父虑,2. 在調用了函數本身该酗。第二個函數和上面相反授药。所以士嚎,我們可以用上面的函數來重構單身狗的例子:
Function.prototype.after = function(afterfn) {
const self = this;
return function () {
const ret = self.apply(this, arguments);
afterfn.apply(this, arguments);
return ret;
}
}
const singleDogSay = function () {
console.log('我是一只單身狗');
}
const decorateGfriend = function () {
console.log('我裝飾了一個女朋友');
}
const flaunt = function () {
console.log('秀死你們');
}
const me = singleDogSay.after(decorateGfriend).after(flaunt);
me(); // 我是一只單身狗 我裝飾了一個女朋友 秀死你們
是不是很爽,想秀多少波都可以悔叽,只要一直after下去莱衩。
接下來,我們來思考一個問題娇澎,裝飾者模式能給我們日常的開發(fā)中帶來哪些好處呢笨蚁?
可以把行為依照職責分成更細的粒度,隨后將它們合并到一起趟庄,這有助于我們編寫一個松耦合高復用的系統(tǒng)
下面是兩個啟發(fā)性例子:
1括细、
大家或許都接觸過“埋點”,一種統(tǒng)計的方式戚啥。比如想獲取有多少用戶點擊了這個活動的按鈕奋单,會在這個按鈕點擊時發(fā)送一條統(tǒng)計數據到服務器。
按照正常思維大家會寫這樣的代碼:
const btn = document.getElementById('J_btn');
const doSomething = function() {
console.log('用戶點擊這個按鈕后發(fā)生的事情');
}
const sendInfo = function() {
console.log('發(fā)送埋點信息');
}
btn.onClick = function() {
doSomething();
sendInfo();
}
上面這段代碼違反了單一職責原則猫十,這個按鈕要做的事情僅僅是doSomething里面的览濒,但是它不得不在onClick實踐中又執(zhí)行了發(fā)送埋點信息這一個操作。所以拖云,我們需要將它贷笛, 分開,而裝飾者模式正好為我們提供了完美的解決方案:
Function.prototype.after = function(afterfn) {
const self = this;
return function () {
const ret = self.apply(this, arguments);
afterfn.apply(this, arguments);
return ret;
}
}
const btn = document.getElementById('J_btn');
let doSomething = function() {
console.log('用戶點擊這個按鈕后發(fā)生的事情');
}
const sendInfo = function() {
console.log('發(fā)送埋點信息');
}
doSomething = doSomething.after(sendInfo);
btn.onClick = doSomething;
這樣就很清晰了宙项,按鈕點擊后只是執(zhí)行doSomthing里面的內容乏苦,而埋點只不過是“附帶”執(zhí)行的操作,在代碼可讀性方面一目了然尤筐,而且兩個函數各自的修改并不會對其他函數造成影響邑贴,完美符合單一職責原則。
2叔磷、
表單驗證
大家應該都編寫過表單驗證的程序:在用戶填寫好一堆表單數據后拢驾,在上傳至服務器前要對表單數據進行校驗,比如改基,用戶名密碼是否為空之類繁疤。遇到這種情況,很直觀的思路是寫下面這樣的代碼實現:
const name = document.getElementById('name');
const password = document.getElementById('password');
const submit = document.getElementById('submit');
submit.onClick = function() {
if (name === '') {
console.log('用戶名不能為空');
return;
}
if (password === '') {
console.log('密碼不能為空');
return;
}
里面校驗的邏輯可以抽出來秕狰,所以變成這樣:
const name = document.getElementById('name');
const password = document.getElementById('password');
const submit = document.getElementById('submit');
const validate = function () {
if (name === '') {
console.log('用戶名不能為空');
return false;
}
if (password === '') {
console.log('密碼不能為空');
return false;
}
return ture;
}
submit.onClick = function() {
if (!validate()) return;
console.log('開始上傳');
}
但是同樣違反了單一職責原則稠腊,上傳只做上傳的事情就OK,不應該參雜校驗的邏輯鸣哀,我們可以用裝飾者模式改寫如下:
const name = document.getElementById('name');
const password = document.getElementById('password');
const submit = document.getElementById('submit');
Function.prototype.before = function(beforefn) {
const self = this;
return function () {
if (!beforefn.apply(this, arguments)) {
return;
}
return self.apply(this, arguments);
}
}
const validate = function () {
if (name === '') {
console.log('用戶名不能為空');
return false;
}
if (password === '') {
console.log('密碼不能為空');
return false;
}
return ture;
}
let forSubmit = function () {
console.log('開始上傳');
}
submit.onClick = forSubmit.before(validate);;
咋們稍稍改了一下before函數架忌,使其前面執(zhí)行的函數返回為false時不執(zhí)行后面的函數。用裝飾者模式重構一波后我衬,是不是感覺很清爽叹放。饰恕。。
最后打兩個廣告:
酷家樂內推歡迎找我井仰,郵箱:chonger@qunhemail.com
單身妹子歡迎找我埋嵌,微信:mz975066610^?_?^