實例說明MVC,MVP,MVVM架構
從一個簡單的例子去研究這三個架構。
注意,MVC,MVP,MVVM中的C,P,VM,下文都要controller指代。
需求如下
界面上顯示100,以及兩個按鈕,其中一個點一下加1,另外一個點一下減1
如圖
誠然,這麼簡單的需求,並不需要用什麼架構去完成,可是如果是複雜的需求,要長篇大論才能說完,所以只拿簡單的來做例子,實際開發中,你在完成一個需求之前,是需要好好掂量是否要用架構,要的話,用什麼架構(不局限於這三個),架構裡面又要用什麼設計模式等等。經過我的實踐,發現,即使是架構改變了,view是可以完全不變的,所以先展現view層的代碼。
html部分
<span id="text">100</span>
<button id="upBtn">up</button>
<button id="downBtn">down</button>
js部分
function $(id) {
return document.querySelector(`#${id}`);
}
function View(controller) {
const upBtn = $("upBtn");
const downBtn = $("downBtn");
const textSpan = $("text");
this.render = function(model) {
textSpan.innerHTML = model.getValue();
}
upBtn.onclick = controller.up;
downBtn.onclick = controller.down;
}
render方法是核心,方法名稱不能改(後面要依賴這個render方法),其中要實現數據的展示邏輯,然後是一些點擊事件的綁定
MVC
model層
function Model() {
let value = 100;
this.up = function() {
value += 1;
};
this.down = function() {
value -= 1;
};
this.getValue = function() {
return value;
};
}
保存數據,並提供訪問,修改數據的方法,如果僅僅是這樣,那麼當model改變時,view是不知道的,所以需要讓model去通知view,我數據改變了,你要更新了。怎麼做呢?利用觀察者模式。在model中,增加一個數組views,去保存這個model對應的視圖,在修改數據的時候,遍歷views數組,調用每個view的render方法,參數是自己。
修改後的model
function Model() {
let value = 100;
const self = this;
const views = [];
this.up = function() {
value += 1;
};
this.down = function() {
value -= 1;
};
this.getValue = function() {
return value;
};
this.broadcast = function() {
views.forEach(view => view.render(self));
};
this.subscribe = function(cb) {
views.push(cb);
}
}
仔細看修改後的model,雖然增加了通知的方法(broadcast),但是在修改數據的方法(up和down)中並沒有去通知視圖。這個工作是由controller承擔的,另外把view註冊到model中,也是controller做的。
controller層
function Controller() {
let view = null;
let model = null;
this.up = function() {
// 修改數據
model.up();
// 通知視圖
model.broadcast();
};
this.down = function() {
model.down();
model.broadcast();
}
this.init = function() {
view = new View(this);
model = new Model();
// 把視圖註冊到model中
model.subscribe(view);
}
}
可以看到,controller把自己傳給了view去創建視圖,同時保存引用,創建model後,把view註冊到model中。同時實現了,改變數據,通知視圖的工作。
請一定要好好理解MVC,後面的MVP,MVVM都只是稍加修改而已。
MVP
在MVC中,改變數據,通知視圖,都是在controller做的,註冊視圖,以及通知視圖,這兩個方法的實現,都是model完成的,既然model負責數據處理,這兩個工作實際上和改變數據是沒關係的,把他們都轉移到controller中,不僅可以讓model層專註於數據處理,同時也方便多個視圖共用一個controller
model層
function Model() {
let value = 100;
this.up = function() {
value += 1;
};
this.down = function() {
value -= 1;
};
this.getValue = function() {
return value;
};
}
model層更小了,刪除了註冊,通知方法,只保存數據和提供獲取,修改數據的方法
controller層
function Controller() {
let views = [];
let model = null;
function broadcast() {
views.forEach(view => view.render(model));
}
this.up = function() {
model.up();
broadcast();
};
this.down = function() {
model.down();
broadcast();
}
this.init = function() {
views.push(new View(this));
model = new Model();
}
}
controller,增加了廣播方法,該方法的實現和調用都在controller中,另外,如果想多個視圖共用一個controller,如果這多個視圖都是同一個model,上面代碼能夠勝任,如果是這多個視圖是不同的model,那就要自己去實現好view和model的對應關係了(要用map來存儲對應關係,一個數組做不到)。
MVVM
可以看到,在MVP中,model也有一個up方法,controller也有一個up方法,只是增加了一個廣播方法的調用。是不是有些重複呢?把這兩個類似的方法整合到controller,model只負責保存數據,不實現修改數據的邏輯,這就是MVVM了,極大地精簡model
model層
function Model() {
let value = 100;
this.getValue = function() {
return value;
};
this.setValue = function(v) {
value = v;
}
}
其實,不用函數,單純地用一個變數,也是可以的,但是為了view層不變,view層中依賴model的getValue方法,所以這裡還是用函數去實現model
controller層
function Controller() {
let views = [];
let model = null;
function broadcast() {
views.forEach(view => view.render(model));
}
this.up = function() {
model.setValue(model.getValue() + 1);
broadcast();
};
this.down = function() {
model.setValue(model.getValue() - 1);
broadcast();
}
this.init = function() {
views.push(new View(this));
model = new Model();
}
}
精簡model的代價是controller要做更多的事情,實現修改數據的邏輯,通知視圖。如果用框架,react或者vue,通知視圖這部分框架會幫你實現,只要實現數據修改的邏輯就好了。
至此,三個架構都講完了,如果錯誤,歡迎討論。
更多優質內容推薦:
2017優就業就業促進計劃:http://www.ujiuye.com/zt/jycj/?wt.bd=zdy35845tt
中公教育「勤工儉學計劃」,給你一個真正0元學習IT的機會!
http://www.ujiuye.com/zt/qgjx/?wt.bd=zdy35845tt
IT職業教育:http://xue.ujiuye.com/
※演算法的複雜度學習筆記
※Eclipse連接SQL Server 2008資料庫以及問題總結
※JPEG流封裝AVI視頻
※設計模式學習筆記 之「多用組合,少用繼承」 C 代碼
※大話數據結構——使用棧實現簡單的四則運算
TAG:IT優就業 |