你將在本文中,了解什么是設(shè)計模式!
你將了解設(shè)計模式有什么優(yōu)點!
你將了解兩種設(shè)計模式-觀察者模式和裝飾模式!
一、什么是設(shè)計模式
什么是設(shè)計模式咸产?其實簡單可以理解為,設(shè)計模式其實是一套理論仲闽,它可以提高代碼的可重用性脑溢,增強系統(tǒng)的可維護性,以及解決一系列的復(fù)雜問題赖欣。模式的另一種解釋就是一個我們?nèi)绾谓鉀Q問題的模版屑彻,提供解決問題的一種方案。
二顶吮、設(shè)計模式的優(yōu)點
模式是行之有效的一種解決方法:通過長期以來前輩們提供固定的解決方案在軟件開發(fā)中出現(xiàn)的問題社牲。
模式是可以很容易地重用:一個模式通常反映解決一些問題可以開箱即用的方法。
模式善于表達:一般有一組結(jié)構(gòu)和詞匯可以非常優(yōu)雅地幫助表達相當大的解決方案悴了。
注:模式不是標準的解決方案搏恤,模式也不能解決所有的設(shè)計問題,也不能代替我們優(yōu)秀的軟件設(shè)計師湃交。模式的角色僅僅提供給我們一個解決方案熟空。
三、JavaScript設(shè)計模式
1搞莺、JavaScript設(shè)計模式之觀察者模式:
先說一下個人理解:觀察者模式又稱“發(fā)布-訂閱(Publish/Subscribe)”息罗,發(fā)布與訂閱是兩個不同對象的功能。在具體編程中才沧,發(fā)布者有了新的內(nèi)容迈喉,需要向訂閱者推送數(shù)據(jù),若訂閱者退訂了則要對發(fā)布者中的訂閱者列表進行更新温圆。
觀察者模式主要應(yīng)用于對象之間一對多的依賴關(guān)系挨摸,當一個對象發(fā)生改變時,多個對該對象有依賴的其他對象也會跟著做出相應(yīng)改變捌木,這就非常適合用觀察者模式來實現(xiàn)。使用觀察者模式可以根據(jù)需要增加或刪除對象嫉戚,解決一對多對象間的耦合關(guān)系刨裆,使程序更易于擴展和維護澈圈。
應(yīng)用場景:
【1】微信公眾號訂閱,多個讀者訂閱一個微信公眾號帆啃,一旦公眾號有更新瞬女,多個讀者都會收到更新。(郵箱訂閱同理)
【2】在《大話設(shè)計模式》一書中努潘,提到類似的情況:如果針對發(fā)布者內(nèi)容而訂閱者要做不同的事情呢诽偷?比如一個按鈕和三個矩形,點擊按鈕的時候疯坤,第一個矩形增加寬度报慕,第二個矩形增加高度,第三個矩形則變成圓角矩形又該怎么做呢压怠?當然我們可以在三個矩形的update內(nèi)部寫具體的實現(xiàn)代碼眠冈,但是這update豈不是沒有一個具體的功能描述了嗎?該書中用“事件委托”解決了這個問題(此處事件委托和DOM中的事件委托應(yīng)該是兩碼事)菌瘫,我個人理解這個“事件委托”在javascript中可以用一個數(shù)組表示蜗顽,然后里面放各個訂閱者的不同名字的update,然后一一調(diào)用雨让。
代碼解讀:
--發(fā)布者相關(guān)信息解讀//發(fā)布者
function Publisher(){
this.observers = [];
this.state = "";
}
//增加
Publisher.prototype.addOb=function(observer){
var flag = false;
for (var i = this.observers.length - 1; i >= 0; i--) {
if(this.observers[i]===observer){
flag=true;
}
};
if(!flag){
this.observers.push(observer);
}
return this;
}
//移除
Publisher.prototype.removeOb=function(observer){
var observers = this.observers;
for (var i = 0; i < observers.length; i++) {
if(observers[i]===observer){
observers.splice(i,1);
}
};
return this;
}
//更新
Publisher.prototype.notice=function(){
var observers = this.observers;
for (var i = 0; i < observers.length; i++) {
observers[i].update(this.state);
};
}
--訂閱者相關(guān)信息解讀//訂閱者
(訂閱者功能很簡單雇盖,只需要一個更新(update)功能,但是每一個訂閱者的更新功能可能不一樣栖忠,例如應(yīng)用場景中提到的【2】)
function Subscribe(){
this.update = function(data){
console.log(data);
};
}
--為了滿足訂閱者不同的需求崔挖,可以為每一個訂閱者的實例設(shè)置單獨的update。
//實際應(yīng)用
var obA = new Subscribe(),
obB = new Subscribe();
var pba = new Publisher();
pba.addOb(obA);
pba.addOb(obB);
obA.update = function(state){
console.log(state+"hello!");
}
obB.update = function(state){
console.log(state+"world!");
}
pba.state = "open ";
pba.notice();
so--彩蛋
一般pm的需求肯定不會這樣提娃闲。mrd文檔一般為第一個input框輸入中文姓名拼音虚汛,第二個input框(不可寫)自動“0881+姓名拼音”,第三個文本框(不可寫)自動生成郵箱“拼音+888@163.com”
部分代碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div>
<label>員工姓名拼音:<input type="text" id="pba" placeholder="請輸入員工姓名拼音" /></label><br /><br />
<label>生成編號:<input type="text" id="oba" readonly /></label>
<label>生成郵箱:<input type="text" id="obb" readonly /></label>
</div>
<script type="text/javascript">
//發(fā)布者
function Publisher(obj){
this.observers = [];
var state = obj.value; //讓該內(nèi)容不能直接訪問
//新增兩個對于state的操作 獲取/更新
this.getState=function(){
return state;
}
this.setState=function(value){
state = value;
this.notice();
}
this.obj = obj;
}
Publisher.prototype.addOb=function(observer){
var flag = false;
for (var i = this.observers.length - 1; i >= 0; i--) {
if(this.observers[i]===observer){
flag=true;
}
};
if(!flag){
this.observers.push(observer);
}
return this;
}
Publisher.prototype.removeOb=function(observer){
var observers = this.observers;
for (var i = 0; i < observers.length; i++) {
if(observers[i]===observer){
observers.splice(i,1);
}
};
return this;
}
Publisher.prototype.notice=function(){
var observers = this.observers;
for (var i = 0; i < observers.length; i++) {
observers[i].update(this.getState());
};
}
//訂閱者
function Subscribe(obj){
this.obj = obj;
this.update = function(data){
this.obj.value = data;
};
}
//實際應(yīng)用
var oba = new Subscribe(document.querySelector("#oba")),
obb = new Subscribe(document.querySelector("#obb"));
var pba = new Publisher(document.querySelector("#pba"));
pba.addOb(oba);
pba.addOb(obb);
oba.update = function(state){
this.obj.value = “0881”+state;
}
obb.update = function(state){
this.obj.value = state+“888@163.com”;
}
pba.obj.addEventListener('keyup',function(){
pba.setState(this.value);
});
</script>
</body>
</html>
2皇帮、JavaScript設(shè)計模式之裝飾者模式:
先說一下個人理解:裝飾者模式重點在裝飾卷哩,同樣都是女生,如果其中一個女生化個妝涂個口紅打扮(裝飾)一下即可秒變女神(奪人眼球的功能)属拾。
其實将谊,實現(xiàn)裝飾者模式的其中一個方法是使得每個裝飾者成為一個對象,并且該對象包含了應(yīng)該被重載的方法渐白。每個裝飾者實際上繼承了目前已經(jīng)被前一個裝飾者進行增強后的對象尊浓。每個裝飾方法在“繼承的對象”上調(diào)用了同樣的方法并獲取其值,此外它還繼續(xù)執(zhí)行了一些操作纯衍。
應(yīng)用場景:
【1】之前項目遇到過的栋齿,創(chuàng)建會員卡,會員卡的外觀以及會員卡詳情都是一樣的功能。但是瓦堵,對于不同的用戶會和會員卡有不一樣的聯(lián)系基协,例如:未激活,未領(lǐng)取菇用,設(shè)置默認卡等澜驮。可以針對會員卡這個對象惋鸥,給予不一樣的修飾函數(shù)杂穷。若后面要對該會員卡還有其他相應(yīng)的交互,直接裝飾即可~
【2】假設(shè)我們在編寫一個飛機大戰(zhàn)的游戲卦绣,隨著經(jīng)驗值的增加耐量,我們操作的飛機對象可以升級成更厲害的飛機,一開始這些飛機只能發(fā)射普通的子彈迎卤,升到第二級時可以發(fā)射導(dǎo)彈拴鸵,升到第三級時可以發(fā)射原子彈。
部分代碼如下:
var Plane = function(){};
Plane.prototype.fire = function(){
console.log( '發(fā)射普通子彈' );
}
var MissileDecorator = function( plane ){
this.plane = plane;
}
MissileDecorator.prototype.fire = function(){
this.plane.fire();
console.log( '發(fā)射導(dǎo)彈' );
}
var AtomDecorator = function( plane ){
this.plane = plane;
}
AtomDecorator.prototype.fire = function(){
this.plane.fire();
console.log( '發(fā)射原子彈' );
}
var plane = new Plane();
plane = new MissileDecorator( plane );
plane = new AtomDecorator( plane );
plane.fire();// 分別輸出: 發(fā)射普通子彈蜗搔、發(fā)射導(dǎo)彈劲藐、發(fā)射原子彈
導(dǎo)彈類和原子彈類的構(gòu)造函數(shù)都接受參數(shù)plane 對象,并且保存好這個參數(shù)樟凄,在它們的fire方法中聘芜,除了執(zhí)行自身的操作之外,還調(diào)用plane 對象的fire 方法缝龄。
因為裝飾者對象和它所裝飾的對象擁有一致的接口汰现,所以它們對使用該對象的客戶來說是透明的,被裝飾的對象也并不需要了解它曾經(jīng)被裝飾過叔壤,這種透明性使得我們可以遞歸地嵌套任意多個裝飾者對象瞎饲。