摘要:寫代碼容易,寫出優(yōu)雅的代碼難,寫易于維護(hù)的容易擴(kuò)展的結(jié)構(gòu)清晰的代碼應(yīng)該是每位開發(fā)者努力的目標(biāo),而學(xué)習(xí)設(shè)計模式,合理的的使用能讓我們離這個目標(biāo)更進(jìn)一步。
寫代碼容易,寫出優(yōu)雅的代碼難,寫易于維護(hù)的、容易擴(kuò)展的、結(jié)構(gòu)清晰的代碼應(yīng)該是每位開發(fā)者努力的目標(biāo),而學(xué)習(xí)設(shè)計模式,合理的的使用能讓我們離這個目標(biāo)更進(jìn)一步。最近看了《Javascript設(shè)計模式與開發(fā)實(shí)踐》這本書,一言以蔽之,真不錯的一本書,在這里總結(jié)一下書中介紹的主要的在JavaScript中我們可以用到的一些設(shè)計模式。設(shè)計模式的思想是值得反復(fù)咀嚼、思考的,在以后的業(yè)務(wù)實(shí)現(xiàn)中,應(yīng)該結(jié)合這些思想,加以合理的使用
更多內(nèi)容
單例模式單例模式保證類只有一個實(shí)例,并提供一個訪問它的全局訪問點(diǎn)
js中實(shí)現(xiàn)
function getSingle(fn){ let result return function (){ return result || (result=fn.apply(this,arguments)) } }策略模式
解決一個問題的多個方法,將每種方法獨(dú)立封裝起來,相互可以替換
一個基于策略模式的程序至少由兩部分組成,一個是一組策略類,策略類封裝了具體的算法,并負(fù)責(zé)具體的計算過程,一個是環(huán)境類,環(huán)境類接受客戶的請求,隨后把請求委托給某個策略類
策略模式的一個使用場景:表單驗(yàn)證,將不同驗(yàn)證規(guī)則封裝成一組策略,避免了多重條件判斷語句
一句經(jīng)典的話:在函數(shù)作為一等對象的語言中,策略模式是隱性的,策略就是值為函數(shù)的變量
例子:
const S = (salary)=>{ return salary * 4 } const A = (salary)=>{ return salary * 3 } const B = (salary)=>{ return salary * 2 } const calculate = (fun,salary)=>{ return fun(salary) } calculate(S,1000)代理模式
代理模式為一個對象提供一個代用品或占位符,以便控制對它的訪問
不直接和本體進(jìn)行交互,而是在中間加入一層代理,代理來處理一些不需要本體做的操作
var myImage=function(){ var imgNode=document.createElement("img") document.body.appendChild(imgNode) return { setImg(src){ imgNode.src=src } } } var proxyImg=function(){ var img =new Image() img.onload=function(){ myImage.setSrc(this.src) } return { setImg(src){ myImage.setSrc(‘loading.png’) img.src=src } } }
代理的意義
對單一職責(zé)原則的一種表現(xiàn),單一職責(zé)原則指的是,一個函數(shù)或類應(yīng)該只負(fù)責(zé)一件事,如何一個函數(shù)職責(zé)太多,等于把這些職責(zé)耦合在了一起,當(dāng)一部分需要改動時,很有可能就影響到了函數(shù)的其他部分
觀察者和發(fā)布訂閱模式觀察者和發(fā)布、訂閱模式使程序的兩部分不必緊密耦合在一起,而是通過通知的方式來通信
觀察者模式
一個對象維持一系列依賴于它的對象,當(dāng)對象狀態(tài)發(fā)生改變時主動通知這些依賴對象
這里注意是對象直接管理著依賴列表,這點(diǎn)也是觀察者模式和發(fā)布、訂閱模式的主要區(qū)別
class Subject{ constructor(){ this.observers=[] } add(observer){ this.observers.push(observer) } notify(data){ for(let observer of this.observers){ observer.update(data) } } } class Observer{ update(){ } }
觀察者模式的缺點(diǎn)是對象必須自己維護(hù)一個觀察者列表,當(dāng)對象狀態(tài)有更新時,直接調(diào)用其他對象的方法,所以,在使用中,我們一般采用一種變形方式,即發(fā)布訂閱模式
發(fā)布訂閱模式
該模式在主題和觀察者之間加入一層管道,使得主題和觀察者不直接交互,發(fā)布者將內(nèi)容發(fā)布到管道,訂閱者訂閱管道里的內(nèi)容,目的是避免訂閱者和發(fā)布者之間產(chǎn)生依賴關(guān)系
class Pubsub{ constuctor(){ this.pubsub={} this.subId=-1 } publish(topic,data){ if(!this.pubsub[topic]) return const subs=this.pubsub[topic] const len=subs.length while(len--){ subs[len].update(topic,data) } } /** * topic {string} * update {function} */ subscribe(topic,update){ !this.pubsub[topic] && (this.pubsub[topic]=[]) this.subId++ this.pubsub[topic].push({ token:this.subId, update }) } unsubscribe(token){ for(let topic in this.pubsub){ if(this.pubsub.hasOwnProperty(topic)){ const current=this.pubsub[topic] for(let i=0,j=current.length;i發(fā)布訂閱模式是在框架設(shè)計中經(jīng)常使用的一種設(shè)計模式,angularjs中的自定義事件,Rxjs,狀態(tài)管理的redux等都能看到它的身影
命令模式命令模式的命令指的是一個執(zhí)行某些特定事情的指令命令模式最常見的使用場景是:有時候需要向某些對象發(fā)送請求,但是不知道請求的接受者是誰,也不知道被請求的操作是什么。此時希望用一種松耦合的方式來設(shè)計程序,是使得請求發(fā)送者和接受者消除彼此之間的耦合關(guān)系
命令模式的由來,其實(shí)是回調(diào)函數(shù)的一個面向?qū)ο蟮奶娲?/strong>
一句話來說,命令模式就是用一個函數(shù)來包裹一個具體的實(shí)現(xiàn),這個函數(shù)統(tǒng)一定義了一個execute方法來調(diào)用具體的實(shí)現(xiàn)方法,而請求者只要和這個命令函數(shù)交流就行
享元模式享元模式顧名思義,共享一些單元,用于優(yōu)化重復(fù)、緩慢及數(shù)據(jù)共享效率較低的代碼應(yīng)用:一是用于數(shù)據(jù)層,處理內(nèi)存中保存的大量相似對象的共享數(shù)據(jù),二是用于DOM層,事件代理
在享元模式中,有個有關(guān)兩個狀態(tài)的概念-內(nèi)部和外部
內(nèi)部狀態(tài)存儲于對象內(nèi)部,可以被一些對象共享,獨(dú)立于具體的場景,通常不會變
外部狀態(tài)根據(jù)場景而變化
剝離了外部狀態(tài)的對象成為共享對象,外部狀態(tài)在必要時被傳入共享對象來組成一個完整的對象
使用享元模式的幾個步驟:
以書中文件上傳的例子描述
剝離外部狀態(tài)
class Upload{ constructor(type){ this.uploadType=type } delFile(id){ uploadManager.setExternalState(id,this) //這里就是組裝外部狀態(tài)來使共享對象變成一個具體的對象 if(this.fileSize<3000){ //直接刪除 return } //彈窗詢問確認(rèn)刪除? } }使用工廠進(jìn)行對象實(shí)例化
var UploadFactory=(function(){ const flyWeightObjs={} return { create(uploadType){ if(flyWeightObjs[uploadType]){ return flyWeightObjs[uploadType] } return flyWeightObjs[uploadType]=new Upload(uoloadType) } } })()使用管理器封裝外部狀態(tài)
var uploadManager=(function(){ var uploadDatabase={} return { add(id,uploadType,fileSize,fileName){ var flyWeightObj=UploadFactory.create(uploadType) //那個被共享的對象 //創(chuàng)建結(jié)點(diǎn)... //刪除操作 dom.onclick=function(){ flyWeightObj.delFile(id) //這個共享在步驟1中會被組合,可以看到,只有在刪除操作的時候,我們才需要那些外部狀態(tài) } uploadDatabase[id]={ fileName, fileSize, dom } return flyWeightObj }, setExternalState(id,flyWeight){ var externalState=uploadDatabase[id] Object.assign(flyWeight,externalState) } } })()責(zé)任鏈模式將一個請求以此傳遞給多個函數(shù),若請求符合當(dāng)前函數(shù)要求,則當(dāng)前函數(shù)處理,否則,傳給下一個很好很強(qiáng)大
責(zé)任鏈模式可以很好的避免大量的if,else if,else
if (Function.prototype.chainAfter) { throw new Error("the chainAfter method already exist") } else { Function.prototype.chainAfter = function (fn) { return (...args) => { const ret = this.apply(this, [...args, () => { return fn && fn.apply(this, args) }]) if (ret === "NEXT") { return fn && fn.apply(this, args) } return ret } } } /** * example * class Test{ * * test(...args){ * alert("test") * return "NEXT" * } * * test1(...args){ * * setTimeout(()=>{ * alert("test1") * args.pop()() * }) * } * * test2(...args){ * alert("test2") * } * * $onInit(){ * const chain = this.test.bind(this) * .chainAfter(this.test1.bind(this)) * .chainAfter(this.test2.bind(this)) * chain(1,2,3) * } * } * */裝飾者模式在不改變原有函數(shù)或?qū)ο蠊δ艿幕A(chǔ)上,給它們新加功能用AOP裝飾函數(shù)
if (Function.prototype.before) { throw new Error("the before method already exist") } else { Function.prototype.before = function (beforefn) { return () => { if (beforefn.apply(this, arguments)) { this.apply(this, arguments) } } } } if (Function.prototype.after) { throw new Error("the after method already exist") } else { Function.prototype.after = function (afterfn) { return () => { this.apply(this, arguments) afterfn.apply(this, arguments) } } }狀態(tài)模式允許一個對象在其內(nèi)部狀態(tài)改變時改變它的行為,對象看起來似乎修改了它的類要點(diǎn):將狀態(tài)封裝成獨(dú)立的函數(shù),并將請求委托給當(dāng)前的狀態(tài)對象,當(dāng)對象的內(nèi)部狀態(tài)改變時,會帶來不同的行為變化
電燈的例子:
一個按鈕控制電燈的開關(guān),按一下是開,再按一下是關(guān)
初始實(shí)現(xiàn):
class Light{ constructor(){ this.state="off", this.button=null } init(){ //創(chuàng)建按鈕結(jié)點(diǎn) ..... this.button.onClick=()=>{ this.btnPressed() } } btnPressed(){ if(this.state=="off"){ this.state="on }else { this.state="off" } } }這段代碼的缺點(diǎn)就是不易擴(kuò)展,當(dāng)要加入一種閃動的狀態(tài)時,就要修改btnPressed中的代碼
使用狀態(tài)模式改寫
class Light{ constructor(){ this.state=FSM.off, this.button=null } init(){ //創(chuàng)建按鈕結(jié)點(diǎn) ..... this.button.onClick=()=>{ this.state.btnPressed.call(this) } } } const FSM={ on:{ btnPressed(){ //處理 this.state=FMS.on } }, off:{ btnPressed(){ //處理 this.state=FMS.off } } }
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/51533.html
摘要:寫代碼容易,寫出優(yōu)雅的代碼難,寫易于維護(hù)的容易擴(kuò)展的結(jié)構(gòu)清晰的代碼應(yīng)該是每位開發(fā)者努力的目標(biāo),而學(xué)習(xí)設(shè)計模式,合理的的使用能讓我們離這個目標(biāo)更進(jìn)一步。 寫代碼容易,寫出優(yōu)雅的代碼難,寫易于維護(hù)的、容易擴(kuò)展的、結(jié)構(gòu)清晰的代碼應(yīng)該是每位開發(fā)者努力的目標(biāo),而學(xué)習(xí)設(shè)計模式,合理的的使用能讓我們離這個目標(biāo)更進(jìn)一步。最近看了《Javascript設(shè)計模式與開發(fā)實(shí)踐》這本書,一言以蔽之,真不錯的一本...
摘要:但是,構(gòu)造函數(shù)注意是大寫的有點(diǎn)特別。構(gòu)造函數(shù)接受的參數(shù)中,第一個是要傳入的參數(shù)名,第二個是函數(shù)內(nèi)的代碼用字符串來表示。 Javascript是一門很吊的語言,我可能學(xué)了假的JavaScript,哈哈,大家還有什么推薦的,補(bǔ)充送那啥邀請碼。 本文秉承著:你看不懂是你SB,我寫的代碼就要牛逼。 1、單行寫一個評級組件 ★★★★★☆☆☆☆☆.slice(5 - rate, 10 - rate...
摘要:但是,構(gòu)造函數(shù)注意是大寫的有點(diǎn)特別。構(gòu)造函數(shù)接受的參數(shù)中,第一個是要傳入的參數(shù)名,第二個是函數(shù)內(nèi)的代碼用字符串來表示。 Javascript是一門很吊的語言,我可能學(xué)了假的JavaScript,哈哈,大家還有什么推薦的,補(bǔ)充送那啥邀請碼。 本文秉承著:你看不懂是你SB,我寫的代碼就要牛逼。 1、單行寫一個評級組件 ★★★★★☆☆☆☆☆.slice(5 - rate, 10 - rate...
摘要:但是,構(gòu)造函數(shù)注意是大寫的有點(diǎn)特別。構(gòu)造函數(shù)接受的參數(shù)中,第一個是要傳入的參數(shù)名,第二個是函數(shù)內(nèi)的代碼用字符串來表示。 Javascript是一門很吊的語言,我可能學(xué)了假的JavaScript,哈哈,大家還有什么推薦的,補(bǔ)充送那啥邀請碼。 本文秉承著:你看不懂是你SB,我寫的代碼就要牛逼。 1、單行寫一個評級組件 ★★★★★☆☆☆☆☆.slice(5 - rate, 10 - rate...
閱讀 2706·2023-04-26 02:02
閱讀 2571·2023-04-25 20:38
閱讀 4098·2021-09-26 09:47
閱讀 3092·2021-09-10 10:50
閱讀 3765·2021-09-07 09:58
閱讀 3326·2019-08-30 15:54
閱讀 2694·2019-08-30 15:54
閱讀 1918·2019-08-29 17:03