版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)
文檔簡介
JS設(shè)計模式
設(shè)計模式是對軟件設(shè)計中普遍存在(反復(fù)出現(xiàn))的各種問題,所
提出的解決方案。設(shè)計模式并不直接用來完成代碼的編寫,而是描述
在各種不同情況下,要怎么解決問題的一種方案。
1.設(shè)計模式是被反復(fù)驗證的解決方案(最佳實踐),可以減少代
碼強耦合、硬編碼,有效的提高代碼的可擴展性和可維護性
2.設(shè)計模式是一種通用方案和思維,它不限制于特定編程語言
3.提供一種通用術(shù)語,方便交流,減少溝通中的理解成本。
設(shè)計模式的原則
做什么事都需要遵循一些準(zhǔn)則,設(shè)計模式也不例外。我們在設(shè)計一些
設(shè)計模式時,一般遵循如下6項基本原則,它們分別是:
Single-responsibilityprinciple(單一職責(zé)原貝!J)
每個軟件模塊都有且只有一個需要被改變的理由。
單一職責(zé)能夠降低模塊的復(fù)雜度,減少功能變更引起的關(guān)聯(lián)風(fēng)險,提
高代碼可讀性和可維護性。
Open-closedprinciple(開放封,閉原則)
系統(tǒng)應(yīng)該對擴展開放,對修改關(guān)閉。
如果軟件系統(tǒng)想要更容易被改變,那么其設(shè)計就必須允許新增代碼來
修改系統(tǒng)行為,而不是只能靠修改原來的代碼。
Liskovsubstitutionprinciple(里斯科夫替代原則)
一個子類應(yīng)該可以替換掉父類并且可以正常工作
Interfacesegregationprinciple(接口隔離原貝!J)
一個類對另一個類的依賴應(yīng)該建立在最小的接口上。
主要告誡軟件設(shè)計師應(yīng)該在設(shè)計中避免不必要的依賴。
Dependencyinversionprinciple(依賴反轉(zhuǎn)原貝!J)
高層策略性的代碼不應(yīng)該依賴實現(xiàn)底層細(xì)節(jié)的代碼,相反,底層細(xì)節(jié)
代碼應(yīng)該依賴高層策略性的代碼。
LawofDemeter(迪米特法則)
減少模塊間的依賴。只有使各個模塊之間的耦合盡量的低,才能提高
代碼的復(fù)用率。
【創(chuàng)建型】
模塊模式
模塊模式是在傳統(tǒng)軟件工程中為類提供私有和公有封裝的方法。
通過這種方式,讓一個對象擁有私有和公有的方法/變量,有效控制
對外暴露的api接口,屏蔽底層處理邏輯,但是由于js曾經(jīng)沒有訪
問修飾符,從技術(shù)的角度來說,我們不能稱js變量為私有和公有。
所以我們需要使用IIFE(即時調(diào)用函數(shù)表達式)、閉包和函數(shù)作用
域的方式來實現(xiàn)js的模塊模式。
varobj=(function(){
varcount=0;
return{
addCount:function(){count++},
getCount:function(){returncount}
})()
IIFE使得obj會獲得function中返回的對象,同時只有對象中的函
數(shù)可以閉包訪問到內(nèi)部的count變量,達到私有的目的。
最終外部采用調(diào)用模塊的公有屬性/方法來訪問和操作私有變量
obj.addCount()//1
obj.getCount()//1
obj.count//undefined
?應(yīng)用場景:
o需要管理大量私有變量/方法,希望屏蔽內(nèi)部處理邏輯,
只對外暴露接口的獨立模塊
?優(yōu)點:
o采用封裝的思想,只有本模塊才能享有私有變量,不會暴
露于外部模塊
。減少對全局作用域的影響,避免命名空間污染;
O模塊化有助于保持代碼的整潔、隔離和條理性。
?缺點:
O無法訪問方法調(diào)用后產(chǎn)生的私有變量
揭示模塊模式
某位同學(xué)(ChristianHeilmann)不滿模塊模式中必須創(chuàng)建新的公
共函數(shù)來調(diào)用私有函數(shù)和變量,所以略微改進了模塊模式為揭示模塊
模式:在本模塊的私有作用域范圍內(nèi)定義所有的方法和變量,將把
返回對象的屬性映射到我們想要公開的私有函數(shù)。
varobj=(function(){
varcount=0;
functionaddCount(){count++}
functiongetCount(){returncount)
return{
addCount:addCount,
getCount:getCount
})0
于模塊模式相比
?優(yōu)點:
O方法從private改為public非常簡單,只需要改屬性映
射
O返回的對象不包含任何函數(shù)定義,公開的函數(shù)和變量一目
了然,有助于提高代碼的可讀性
?缺點:
o導(dǎo)致私有函數(shù)升級困難。如果一個私有函數(shù)引用一個公有
函數(shù),在需要打補丁時,公有函數(shù)是不能被覆蓋的。eg:
functionrmpUrIBuiIder(){
varurlBase=〃http://my.default,domain/”;
var_build=function(relUrl){
returnurlBase+relUrl;
return{
urlBase:_urlBase,
build:build}
)
varbuilder=newrmpUrIBuiIder();
builder.urlBase=/zhttp://stackoverflow,com”;
console.log(builder.build('/questions");
//打印結(jié)果是"http://my.default,domain/questions”
//而不是//http://stackoverflow,com/questions^
單例模式
單例模式限制某個類只能被創(chuàng)建一次,并且需要提供一個全局的訪問
點。如果一個類的實例不存在,單例模式就會創(chuàng)建一個新的類實例。
如果實例存在,它只返回對該對象的引用。對構(gòu)造函數(shù)的任何重復(fù)調(diào)
用都會獲取相同的對象。
js中單例模式可以使用構(gòu)造函數(shù)實現(xiàn),例如:
letinstance=nulljfunctionUser(){
if(instance){
returninstance;
instance=this;
this,name='Peter';
this,age=25;
returninstance;
)
constuserl=newUser();
constuser2=newUser();
console,log(userl===user2);
//打印true
當(dāng)調(diào)用這個構(gòu)造函數(shù)時,它會檢查實例對象是否存在。如果對象不存
在,它就將這個變量賦給實例變量。如果對象存在,它只返回那個對
象。
單例也可以使用模塊模式實現(xiàn),例如:
constsingleton=(function(){
letinstance;
functioninit(){
return(
name:'Peter',
age:24,};
return{
getlnstance:function(){
if(!instance){
instance=init();
■
returninstance;
)|
})0;
constinstanceA=singleton,getlnstance();
constinstanceB=singleton,getlnstance();
console.log(instanceA===instanceB);//打印true
在上面的代碼中,我們通過調(diào)用singleton,getlnstance方法來創(chuàng)
建一個新實例。如果實例已經(jīng)存在,則該方法只是返回這個實例,如
果實例不存在,則調(diào)用initO函數(shù)創(chuàng)建一個新的實例。
小結(jié)
?應(yīng)用場景:
O應(yīng)用中有對象需要是全局的且唯一。比如頁面全局蒙層。
?優(yōu)點:
o適用于單一對象,只生成一個對象實例,避免頻繁創(chuàng)建和
銷毀實例,減少內(nèi)存占用。
?缺點:
O不適用動態(tài)擴展對象,或需創(chuàng)建多個相似對象的場景
簡單工廠模式
簡單工廠模式是一種創(chuàng)建型模式,它不需要指定所創(chuàng)建對象的確
切類或構(gòu)造函數(shù)。由工廠對象提供對外提供通用的工廠方法,然后根
據(jù)我們的特定需求/條件,生成不同的對象,在創(chuàng)建對象的過程中不
用公開實例化的邏輯。
舉例:在系統(tǒng)通知中我們會發(fā)送多種類型的消息,如郵件、微信、
短信、電話等。我們在創(chuàng)建不同消息的時候,可以采用工廠模式。通
過傳入的參數(shù)不同,輸出不同的消息對象。
classEmail{
constructor(options){
this,message=options,message||'我是emial信息';
this,type=options,type||'email',
this.sender=options,user;
this.receiver=options.receiver;
this.sendTime=options.sendTime|newDate()
Ml
I
classWeixin{
constructor(options){
this,msg=options,messageII'我是微信信息';
this,type=options,type||'weixin',
this.sender=options,user;
this.receiver=options,receiver;
classMessageFactory{
create(options){
switch(option,type){
case'emaiT:
returnnewEmail(options);
case'weixin':
returnnewWeixin(options);
default:
returnnull;
Ml
)
在這里我創(chuàng)建了一"1、Email類和一個Weixin類(帶有一些默認(rèn)
值),用于創(chuàng)建新的Email和Weixin對象。我還定義了一個
MessageFactory類,基于options對象中接收到的type屬性創(chuàng)建
和返回一個新的對象。
constfactory=newMessageFactory();
constemailMsg=factory,create({
type:'email',
message:'你好,有興趣了解下安利嗎’,
user:'xiaojia',
receiver:'xxl',
});
constweixinMsg=factory,create({
type:'weixin',
message:‘im賣保險',
user:'xiaojia',
receiver:'xx2',
});
我已經(jīng)創(chuàng)建了一個新的MessageFactory類的對象工廠。之后,我們
可以調(diào)用factory,create方法,傳入一個type屬性值為email
或weixin的options,來創(chuàng)建不同的消息對象。
?應(yīng)用場景:
o需要處理共享多個相同屬性的小型對象/組件
。可以根據(jù)不同的環(huán)境/參數(shù)生成對象的不同實例
o對象的類型已知、有限
?優(yōu)點:
O能解決創(chuàng)建多個相似對象的問題。
?缺點:
O違背了開放封閉原則。每增加一個產(chǎn)品,都需要修改工廠
類的代碼
o對象的類型不知道
工廠模式
在簡單工廠模式中,一個工廠類負(fù)責(zé)所有產(chǎn)品對象的創(chuàng)建,這個
工廠類的職責(zé)大大增加,可能客戶端對于某些產(chǎn)品的創(chuàng)建方式會有不
同的要求,這樣的話,就要不斷的修改工廠類,增加相應(yīng)的判斷邏輯,
不利于后期的代碼維護。所以我們將簡單工廠模式進一步抽象化,實
現(xiàn)工廠模式:讓工廠子類去實現(xiàn)抽象工廠類的接口,由每個具體的工
廠類負(fù)責(zé)創(chuàng)建單獨的產(chǎn)品,如果有新的產(chǎn)品加進來,只需要增加一個
具體的創(chuàng)建產(chǎn)品工廠類和具體的產(chǎn)品類就可以了,不會影響到已有的
其他代碼,代碼量也不會變大,后期維護更加容易,增加了系統(tǒng)的可
擴展性。
我們基于上述MessageFactory的代碼進行修改
classMessageFactory{
create(options){
thrownewErrorC需要create方法’)
I
classEmaiIMsgFactoryextendsMessageFactory{
create(options){
returnnewEmail(options)
HH
)
?應(yīng)用場景:
o需要處理多個復(fù)雜產(chǎn)品對象
。不同的工廠和產(chǎn)品可以提供客戶端不同的服務(wù)或功能
o產(chǎn)品對象的類型會動態(tài)增加
?優(yōu)點:
O克服了簡單工廠違背開放-封閉原則的缺點,后期維護更
加容易。
?缺點:
o每增加一個產(chǎn)品,相應(yīng)的也要增加一個子工廠,加大了額
外的開發(fā)量
原型模式
原型模式主要是用于創(chuàng)建重復(fù)的對象。通俗點講就是創(chuàng)建一個共享的
原型,并通過拷貝這些原型創(chuàng)建新的對象。
js中可以通過Object,create來實現(xiàn)
varmyCar={
name:"FordEscort”,
drive:function(){
console,log("Weeee.I'mdriving!,z);
I^H
panic:function(){
console.logC^Wait.Howdoyoustopthisthing?");
HH
);
//UseObject,createtoinstantiateanewcar
varyourCar=Object,create(myCar);
//Nowwecanseethatoneisaprototypeoftheother
console.log(yourCar.name);
?應(yīng)用場景:
o需要大量復(fù)制已存在的對象,對象間又相互獨立
?優(yōu)點:
o/實現(xiàn)功能吧,沒想出什么優(yōu)點
?缺點:
【結(jié)構(gòu)型】
代理模式
代理模式的核心是為對象提供一個代理對象,來控制對目標(biāo)對象的訪
問,客戶其實訪問的是這個代理對象。這樣代理對象就可以對請求做
出一些處理之后,再將請求轉(zhuǎn)交給本體對象。
代理模式中常見的有保護代理、虛擬代理、緩存代理。
保護代理主要是限制了訪問主體的行為。下面以過濾消息中的敏感信
息作為簡單的例子
//主體,發(fā)送消息functionsendMsg(msg){
console,log(msg);
|
//代理,對消息進行過濾functionproxySendMsg(msg){
//無消息則直接返回
if(typeofmsg==='undefined'){
console.log(,deny,);
return;
//有消息則進行過濾
msg=(''+msg).replace(/敏感信息/g,'****');
sendMsg(msg);
sendMsg('敏感信息');〃敏感信息
proxySendMsgC敏感信息');//****
proxySendMsgO;//deny
虛擬代理主要是在訪問行為中加入一些額外操作,最常見的例子有函
數(shù)防抖。我們的目的是去觸發(fā)fn,但是debounce函數(shù)會對清除老的
timer,并且將fn放到新的timer中。
//函數(shù)防抖,頻繁操作中不處理,直到操作完成之后(再過delay的
時間)才處理functiondebounce(fn,delay){
delay=delay||200;
vartimer=null;
returnfunction(){
vararg=arguments;
//每次操作時,清除上次的定時器
clearTimeout(timer);
timer=null;
//定義新的定時器,一段時間后進行操作
timer=setTimeout(function(){
fn.apply(this,arg);
delay);
IH
};
另外,ES6所提供Proxy構(gòu)造函數(shù)也能夠讓我們輕松的使用代理模式。
?應(yīng)用場景:
O訪問對象比較復(fù)雜,并且需要對訪問行為進行控制
?優(yōu)點:
O依托代理,可額外添加擴展功能,而不修改本體對象,符
合”開發(fā)-封閉原則”
O對象職能粒度細(xì)分,函數(shù)功能復(fù)雜度降低
?缺點:
O額外代理對象的創(chuàng)建,增加部分內(nèi)存開銷
o處理請求速度可能有差別,非直接訪問存在開銷
適配器模式
在生活中我們常常會用到電源適配器、Type-C轉(zhuǎn)接頭,這些器件
都是為了不同的設(shè)備能夠兼容協(xié)作。
適配器模式的目的也是這樣:將一個對象的接口(方法或?qū)傩裕?/p>
轉(zhuǎn)化成客戶希望的另外一個接口(方法或?qū)傩裕?,使得原本由于接?/p>
不兼容而不能一起工作的那些對象可以正常協(xié)作。
工作中最常見的就是各種數(shù)據(jù)格式的轉(zhuǎn)換,以傳遞給不同的插件
方法。
functiondataConvenrt(data){
//dosomething
returndealData
)
適配器模式也非常適用于跨瀏覽器兼容,例如強大的jQuery封
裝了事件處理的適配器,解決跨瀏覽器兼容性問題,極大簡化我們?nèi)?/p>
常編程操作。
functionon(target,event,callback){
if(target.addEventListener){
//標(biāo)準(zhǔn)事件監(jiān)聽
target.addEventListener(event,callback);
}elseif(target.attachEvent){
//IE低版本事件監(jiān)聽
target.attachEvent(event,callback)
}else{
//低版本瀏覽器事件監(jiān)聽
target['on${
event
}']=callback
)
,與代理模式的差異:
O兩者都會在訪問原始對象前對請求數(shù)據(jù)進行處理。但是適
配器的目的是為了兼容不同對象的接口/屬性,而代理模
式是為了控制訪問行為。
?應(yīng)用場景:
o需要兼容不同對象的接口。比如跨瀏覽器兼容、整合第三
方SDK、新老接口兼容
?優(yōu)點:
。兼容性好,保證外部可統(tǒng)一接口調(diào)用
?缺點:
O額外對象的創(chuàng)建,非直接調(diào)用,存在一定的開銷(且不像
代理模式在某些功能點上可實現(xiàn)性能優(yōu)化)。
裝飾器模式
我們在打游戲時,游戲角色會帶上各種buff、debuffo這種屬性并
不是繼承而來,而是游戲過程中動態(tài)增加的。這種場景就非常適合裝
飾器模式。
裝飾器模式可以動態(tài)地給對象添加一些新功能。它是一種“即用即付”
的方式,能夠在不改變對象自身的基礎(chǔ)上,在程序運行期間給對象動
態(tài)地添加職責(zé)。
JS中最簡單的裝飾器就是重寫對象的屬性
varA={
score:10
);
A.score='分?jǐn)?shù):'+A.score;
也可以通過構(gòu)造函數(shù)和原型的方式來實現(xiàn)裝飾器,并且經(jīng)過多重包裝
可以形成一條裝飾鏈
functionPerson(){}
Person,prototype,skill=function(){
console.logC(數(shù)學(xué)');
);
//裝飾器,還會音樂functionMusicDecorator(person){
this,person=person;
I
MusicDecorator.prototype,skill=function(){
this,person,skill();
console.log('音樂’);
};
//裝飾器,還會跑步functionRunDecorator(person){
this.person=person;
|
RunDecorator.prototype,skill=function(){
this,person,skill();
console,log('跑步');
);
varperson=newPerson();
//裝飾一下
varpersonl=newMusicDecorator(person);
//再裝飾一下
personl=newRunDecorator(personl);
person,skill();//數(shù)學(xué)
personl.skill();//數(shù)學(xué)音樂跑步
最新的ECMA中有裝飾器(Decorator)的提案,它是一種與類(class)
相關(guān)的語法,用來注釋或修改類和類方法。有興趣的同學(xué)可以看下阮
一峰的介紹
?應(yīng)用場景:動態(tài)地給對象添加一些新功能,并且不改變對象本
身。
?優(yōu)點:同上
?缺點:/
組合模式
組合模式是為了解決大型/復(fù)雜對象的結(jié)構(gòu)問題,用小的子對象來
構(gòu)建更大的對象,而這些小的子對象本身也許是由更小的“孫對象”
構(gòu)成的。
這種組合有具有一定的要求和條件:1.對象可以用樹形結(jié)構(gòu)來表
示;2.根對象和葉對象屬于同一類,需要具有相同的接口。
最常見的例子有遞歸掃描文件夾中的文件
//文件夾組合對象functionFolder(name){
this,name=name;
this,parent=null;
this,files=口;
|
Folder,prototype={
constructor:Folder,
add:function(file){
file,parent=this;
this,files,push(file);
returnthis;
),
scan:function(){
//委托給葉對象處理
for(vari=0;i<this,files,length;++i){
this.files[i].scan();
HI
■
remove:function(file){
if(typeoffile===,undefined(){
this,files=[];
return;
for(vari=0;i<this,files.length;++i){
if(this,files[i]===file){
this,files.splice(i,1);
};
//文件葉對象functionFile(name){
this,name=name;
this,parent=null;
Ftotype={
constructor:File,
add:function(){
console.log(,文件里面不能添加文件’);
I^H
scan:function(){
varname=[];
varparent=this,parent;
while(parent){
name,unshift(parent,name);
parent=parent,parent;
console,log(name.join(,/'));
);
我們在構(gòu)造組合對象和葉對象后進行實例化,插入數(shù)據(jù)
varweb=newFolder('Web');
varfe=newFolder('前端');
varcss=newFolder('CSS');
varjs=newFolder('js');
varrd=newFolder('后端');
web.add(fe).add(rd);
varfilel=newFile('HTML權(quán)威指南.pdf');
varfile2=newFile('CSS權(quán)威指南.pdf');
varfile3=newFile('JavaScript權(quán)威指南.pdf');
varfile4=newFile('MySQL基礎(chǔ).pdf');
varfile5=newFile('Web安全.pdf');
varfile6=newFile('Linux菜鳥.pdf');
css.add(file2);
fe.add(filel).add(file3).add(css).add(js);
rd.add(file4).add(file5);
web.add(file6);
rd.remove(file4);
//掃描
web.scan();
//結(jié)果
//Web/前端/HTML權(quán)威指南.pdf
//Web/前端/JavaScript權(quán)威指南.pdf
//Web/前端/CSS/CSS權(quán)威指南.pdf
//Web/后端/Web安全.pdf
//Web/Linux菜鳥.pdf
?應(yīng)用場景:
O可以組合成樹形結(jié)構(gòu)的復(fù)雜對象,并且需要對外提供一致
性的操作接口。
O優(yōu)化處理遞歸或分級數(shù)據(jù)結(jié)構(gòu)
?優(yōu)點:
。忽略組合對象和單個對象的差別,對外一致接口使用;
O解耦調(diào)用者與復(fù)雜元素之間的聯(lián)系,處理方式變得簡單。
?缺點
O樹葉對象接口一致,無法區(qū)分,只有在運行時方可辨別;
外觀模式
外觀模式是一種非常簡單而又無處不在的模式。外觀模式對外提
供一個統(tǒng)一高層的方法,來訪問子系統(tǒng)中的一群接口,能夠隱藏其底
層的復(fù)雜性。
通俗點來解釋這個模式:今天瑞瑞準(zhǔn)備叫外賣。單點是一種方式,
但面對各種單品也是無從下手,這時瑞瑞就會選擇看套餐,因為這個
是已經(jīng)搭配好的,并且沒有選擇糾結(jié)。這個套餐就是我們的外觀模式
雛型,把一些細(xì)碎的東西收起來,統(tǒng)一對外開放。
舉個
fcuntiontosllpload(){
//先獲取上傳token
//執(zhí)行上傳
//上傳后將附件數(shù)據(jù)發(fā)送給業(yè)務(wù)后臺服務(wù)
?優(yōu)點:
O簡化接口,易于使用
O使用者與底層代碼解耦
.缺點:
O隱藏底層邏輯,不易調(diào)試
O子系統(tǒng)需要能夠提供穩(wěn)定服務(wù)
?和組合模式的差別:外觀模式內(nèi)部需要知道具體哪幾個對象,
組合模式是取全量葉節(jié)點
?和中介模式的差別:外觀模式處理的是類之間的復(fù)雜依賴關(guān)系,
中介模式處理的是對象之間復(fù)雜的通信機制
【行為型】
觀察者模式
觀察者模式是一種行為型模式,關(guān)注的是對象之間的通訊,觀察者模
式就是觀察者和被觀察者之間的通訊,主要用于一個對象(目標(biāo))去
維持多個依賴于它的對象(觀察者),將相關(guān)變更的事件自動通知給
他們的場景。
functionSubject(){this.observerList=[]//初始化觀察者
隊列}functionObserver(){this,update=
function(ctx){//dosomethingwihtctx}}//增加
觀察者Subject,prototype.addObserver=
function(observer){this.observerList.push(observer)}//
通知觀察者Subject,prototype,notify=
function(ctx){this.observerList.forEach(observer=>{
observer.update(ctx)})}
?應(yīng)用場景:
o對一個對象狀態(tài)的更新,需要其他對象同步更新,而且其
他對象的數(shù)量動態(tài)可變。
O對象僅需要將自己的更新通知給其他對象而不需要知道
其他對象的細(xì)節(jié)。
。比如采購中,尋源結(jié)果審批通過后,會要通知相關(guān)采購負(fù)
責(zé)人、自動創(chuàng)建合同信息等后續(xù)操作。
?優(yōu)點:
O觀察者模式在被觀察者和觀察者之間建立一個抽象的耦
合。被觀察者角色所知道的只是一個具體觀察者列表,每
一個具體觀察者都符合一個抽象觀察者的接口。
O觀察者模式支持廣播通訊。被觀察者會向所有的登記過的
觀察者發(fā)出通知,
?缺點:
O如果一個被觀察者對象有很多的直接和間接的觀察者的
話,同步通知花費時間會很長。
。如果在被觀察者之間有循環(huán)依賴的話,被觀察者會觸發(fā)它
們之間進行循環(huán)調(diào)用,導(dǎo)致系統(tǒng)崩潰。
發(fā)布訂閱模式
其實24種基本的設(shè)計模式中并沒有發(fā)布訂閱模式,他只是觀察者
模式的一個別稱。但是經(jīng)過時間的沉淀,似乎他已經(jīng)強大了起來,已
經(jīng)獨立于觀察者模式,成為另外一種不同的設(shè)計模式。
在現(xiàn)在的發(fā)布訂閱模式中,稱為發(fā)布者的消息發(fā)送者不會將消息
直接發(fā)送給訂閱者,這意味著發(fā)布者和訂閱者不知道彼此的存在。在
發(fā)布者和訂閱者之間存在第三個組件,稱為調(diào)度中心或事件通道,它
維持著發(fā)布者和訂閱者之間的聯(lián)系,過濾所有發(fā)布者傳入的消息并相
應(yīng)地分發(fā)它們給訂閱者。
舉一個例子,你在微博上關(guān)注了A,同時其他很多人也關(guān)注了A,
那么當(dāng)A發(fā)布動態(tài)的時候,微博就會為你們推送這條動態(tài)。A就是發(fā)
布者,你是訂閱者,微博就是調(diào)度中心,你和A是沒有直接的消息往
來的,全是通過微博來協(xié)調(diào)的(你的關(guān)注,A的發(fā)布動態(tài))。
我們每個人在編程的時候都用過發(fā)布訂閱模式,比如DOM事件綁定
addEventListener^vue數(shù)據(jù)雙向綁定。
考慮到面試經(jīng)常問到,我就不寫demo代碼了。。
優(yōu)點:
O相較于觀察者模式,發(fā)布/訂閱發(fā)布者和訂閱者的耦合性
更低
O事件通知分發(fā)是異步的
?缺點:
O發(fā)布者不知道所有訂閱者是否成功收到通知
?和觀察者模式的區(qū)別:
O在觀察者模式中直接通信。然而在發(fā)布訂閱模式中只有
通過消息代理進行通信。
O發(fā)布/訂閱模式相比于觀察者模式多了一個中間媒介,因
為這個中間媒介,發(fā)布者和訂閱者的關(guān)聯(lián)更為松耦合
O觀察者模式大多數(shù)時候是同步的,比如當(dāng)事件觸發(fā),
Subject就會去調(diào)用觀察者的方法。而發(fā)布-訂閱模式大
多數(shù)時候是異步的。
策略模式
策略模式的意義是定義一系列的算法,把它們一個個封裝起來,
并且使它們可相互替換。具體實現(xiàn)是由多個策略類實現(xiàn)具體算法,然
后由一個環(huán)境類來通過請求參數(shù)決定使用哪些策略。
策略模式利用組合、委托等方法,可以有效避免多個if條件語句。
適合用于組合一系列算法,或者組合一系列相同目的的業(yè)務(wù)規(guī)則。
舉個業(yè)務(wù)場景,我們?nèi)绻鶕?jù)不同銷售等級來計算工資,可能
會寫出這么僵硬的代碼:
varcalculateBouns=function(salary,level){
if(level==='A'){
returnsalary*4;
if(level==='B'){
returnsalary*3;
if(level==='C'){
returnsalary*2;
)
};
//調(diào)用如下:
console.log(calculateBouns(4000,'A());//16000
console,log(calculateBouns(2500,'B'));//7500
這段代碼包含多個if-else,并且也缺乏彈性。如果還來個D等
級,或者A等級的計算規(guī)則需要改變,那么就需要在calculateBouns
方法中去修改,違背了開-閉原則。
我們基于策略模式重構(gòu)一下?,F(xiàn)在我們將具體策略封裝起來,可
以看到代碼職責(zé)更新分明,代碼變得更加清晰。并且代碼的可拓展性
更強,增加/修改策略,只需要在策略集合。bj中去維護.
varobj={
"A":function(salary){
returnsalary*4;
“B〃:function(salary){
returnsalary*3;
“C〃:function(salary){
returnsalary*2;
IH
);
varcalculateBouns=function(level,salary){
returnobj[level](salary);
E
console,log(calculateBouns,10000));//40000
策略模式也常用于表單驗證,定義不同的規(guī)則校驗方法,調(diào)用驗證的
時候只需要傳入規(guī)則名即可。
?應(yīng)用場景:
O調(diào)用方依賴1個或多個策略
O業(yè)務(wù)場景有多種條件處理方案
?優(yōu)點:
O減少if-else,代碼更整潔直觀
O提供開放-封閉原則,代碼更容易理解和擴展
?缺點:
O策略集合通常會比較多,需要事先了解定義好所有的情況
。使用方必須理解不同策略的區(qū)別
模板模式
模板模式為了解決不同對象的相同行為的場景,它由兩部分組成:
抽象父類+具體的實現(xiàn)子類。抽象父類定義抽象方法和具體運行策
略,來制定子類方法的運行順序和機制;具體子類來重寫父類的抽象
方法,實現(xiàn)不同的處理邏輯。
舉個業(yè)務(wù)場景例子:在頭條面試的流程是:筆試-技術(shù)面-leader
面-hr面。百度的面試流程也是這樣。那我們就可以用模板模式,創(chuàng)
建一個抽象的面試類,在面試類中定義面試的流程,然后由頭條面試
/百度面試對象繼承和重寫具體的面試步驟內(nèi)容,比如說頭條筆試是
考算法、百度筆試是考css。并且可以通過鉤子函數(shù)來解決是否需要
某些運行步驟。
定義面試抽象類,init為具體子類方法運行策略,其他方法是抽
象方法。
//面試functionInterview(companyName){
this.companyName=companyName
|
Interview,prototype={
constructor:Interview,
//模板,按順序執(zhí)行
init:function(){
this.writtenTest();
this.techTest();
this.leaderTest();
if(this.needHrTest()){//鉤子函數(shù)
this.hrTest();
//筆試
writtenTest:function(){
■thrownewError('必須傳遞writtenTest方法');
//面試
techTest:function(){
■thrownewError('必須傳遞techTest方法');
//leader面
leaderTest:function(){
■thrownewError('必須傳遞leaderTest方法');
//是否需要hr面
needHrTest:function(){
Ireturntrue
//hr面
hrTest:function(){
thrownewError('必須傳遞hrTest方法');
};
子類實現(xiàn)具體抽象方法,并調(diào)用init方法執(zhí)行面試行為。
varTouTiaoInterview=function(){};
TouTiaoInterview.prototype=newInterview();
//子類重寫方法實現(xiàn)自己的業(yè)務(wù)邏輯
TouTiaoInterview.prototype.writtenTest=function(){
console,log(〃先來個紅黑樹〃);
I
TouTiaoInterview.prototype.techTest=function(){
console,log(〃你對什么比較了解“);
)
TouTiaoInterview.prototype.leaderTest=function(){
console,log("leader談笑風(fēng)生“);
|
TouTiaoInterview.prototype.hrTest=function(){
console,log(〃人力資源太不給力了,我等的花兒都謝了!!〃);
|
varTouTiaoInterview=newTouTiaoInterview();
TouTiaoInterview.init();
?應(yīng)用場景:
O在多個子類擁有相同的方法,并且這些方法邏輯相同時
O在程序的主框架相同,細(xì)節(jié)不同的場合下
?優(yōu)點:
O利用模板模式可以將相同處理邏輯的代碼放到抽象父類
中,提高了代碼的復(fù)用性。
O將不同的邏輯放到不同的子類中,通過子類的擴展增加新
的行為,提高了代碼的擴展性。
?缺點:
O類數(shù)量增加間接增加了系統(tǒng)的復(fù)雜性
O因為繼承關(guān)系的自身缺點,如果父類添加一個新的抽象方
法,所有子類都要實現(xiàn)一遍。
狀態(tài)模式
對象的狀態(tài)會影響對象的行為,并且行為中伴隨著狀態(tài)的改變時,
就非常適合狀態(tài)模式。把事物的每種狀態(tài)都封裝成單獨的類,跟此種
狀態(tài)有關(guān)的行為都被封裝在這個類的內(nèi)部
我們每天也都處于狀態(tài)模式下:工作=>睡覺=>工作=>睡覺。那我
們可以定一個person對象和work、sleep兩種狀態(tài)
functionPerson(name){
this,name=namethis,currentstate=null;
//狀態(tài)
this.workState=newWorkState(this)this.sleepState=new
SleepState(this)
this,init()
|
Person,prototype,init=function(){
this,currentstate=this.workState;//設(shè)置初始狀態(tài)
this,currentstate,behaviour0;//開始初始狀態(tài)的行為
E
Person,prototype.setState=function(state){
this,currentstate=state;
this,currentstate,behaviour();
|
//工作狀態(tài)functionWorkState(person){
this,person=person
I
WorkState.prototype,behaviour=function(){
console.log(this.person,name+'上班摸魚了8小時')
//觸發(fā)[睡覺]狀態(tài)
setTimeout(()=>{
this,person.setState(this,person.sleepState);
},
2*1000);
|
//睡覺狀態(tài)functionSleepState(person){
this,person=person
|
SleepStotype,behaviour=function(){
console.log(this.person,name+'睡了了14小時')
//觸發(fā)[睡覺]狀態(tài)
setTimeout(()=>{
this,person.setState(this,person.workState);
I^H
2*1000);
varperson=newPerson('老王')
?應(yīng)用場景:
o對象和外部互動時,會改變內(nèi)部狀態(tài),并且不同的狀態(tài)會
發(fā)生不同的行為
o在運行過程中這個對象的狀態(tài)會經(jīng)常切換
?優(yōu)點
O一個狀態(tài)狀態(tài)對應(yīng)行為,封裝在一個類里,更直觀清晰,
增改方便
O狀態(tài)與狀態(tài)間,行為與行為間彼此獨立互不干擾
。避免事物對象本身不斷膨脹,條件判斷語句過多
?缺點:
O需要將事物的不同狀態(tài)以及對應(yīng)的行為拆分出來,有時候
會無法避免動作拆不明白了,過度設(shè)計
O與策略模式的不同:狀態(tài)模式經(jīng)常會在處理請求的過程中
更改上下文的狀態(tài),而策略模式只是按照不同的算法處理
算法邏輯
中介者模式
程序中存在對象,所有這些對象都按照某種關(guān)系和規(guī)則來通信。
當(dāng)程序的規(guī)模增大,對象會越來越多,它們之間的關(guān)系也越來越復(fù)雜,
難免會形成網(wǎng)狀的交叉引用。當(dāng)改變或刪除其中一個對象的時候,很
可能需要通知所有引用到它的對象。這樣的硬編碼方式會導(dǎo)致代碼和
對象的邏輯關(guān)系難以維護。
□中介者對象可以讓各個對象之間不需要顯示的相互引用,從而使
其耦合松散,而且可以獨立的改變它們之間的交互。所有的相關(guān)對象
都通過中介者對象來通信,而不是互相引用,所以當(dāng)一個對象發(fā)生改
變時,只需要通知中介者對象即可
「采購部就是一個典型的中介者模式。業(yè)務(wù)部門A找采購部提采購
需求,由采購部去發(fā)布公告召集B、C、D
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2026年西北工業(yè)大學(xué)沖擊動力學(xué)及工程應(yīng)用重點實驗室非事業(yè)編科研助理招聘備考題庫附答案詳解
- 高中生評估生物組織培養(yǎng)技術(shù)繁殖瀕危蘭科植物的經(jīng)濟效益課題報告教學(xué)研究課題報告
- 環(huán)保項目評估與治理技術(shù)指南
- 二年級數(shù)學(xué)上冊《表內(nèi)乘法(二)-解決問題》新人教版教學(xué)設(shè)計
- 2025年旅游安全防范措施指南
- 高中生通過地理信息系統(tǒng)技術(shù)分析鄭和船隊航海路線的海上通信信息傳遞特征課題報告教學(xué)研究課題報告
- 2025年環(huán)保行業(yè)五年發(fā)展:碳交易報告
- 供應(yīng)鏈金融業(yè)務(wù)操作與風(fēng)險管理(標(biāo)準(zhǔn)版)
- 教育創(chuàng)新成果學(xué)術(shù)研究承諾書7篇范文
- 我們家的活寶作文600字(7篇)
- 售后服務(wù)流程管理手冊
- 2020-2021學(xué)年新概念英語第二冊-Lesson14-同步習(xí)題(含答案)
- 醫(yī)院信訪維穩(wěn)工作計劃表格
- 地下車庫建筑結(jié)構(gòu)設(shè)計土木工程畢業(yè)設(shè)計
- GB/T 2261.4-2003個人基本信息分類與代碼第4部分:從業(yè)狀況(個人身份)代碼
- GB/T 16601.1-2017激光器和激光相關(guān)設(shè)備激光損傷閾值測試方法第1部分:定義和總則
- PDM結(jié)構(gòu)設(shè)計操作指南v1
- 投資學(xué)-課件(全)
- 獼猴桃優(yōu)質(zhì)栽培關(guān)鍵技術(shù)課件
- 科目一駕考測試題100道
- 兒童吸入性肺炎的診斷與治療課件
評論
0/150
提交評論