加入QQ群:864680898,一起學習進步!點選群名可檢視本人網站,有最新文章!
前端設計模式

設計模式是一套被反複使用的、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是為了重用代碼、讓代碼更容易被他人了解、保證代碼可靠性。 毫無疑問,設計模式于己于他人于系統都是多赢的,設計模式使代碼編制真正工程化,設計模式是軟體工程的基石,如同大廈的一塊塊磚石一樣。項目中合理地運用設計模式可以完美地解決很多問題,每種模式在現實中都有相應的原理來與之對應,每種模式都描述了一個在我們周圍不斷重複發生的問題,以及該問題的核心解決方案,這也是設計模式能被廣泛應用的原因。
單例模式
這種類型的設計模式屬于建立型模式,它提供了一種建立對象的最佳方式。這種模式涉及到一個單一的類,該類負責建立自己的對象,同時確定隻有單個對象被建立。這個類提供了一種通路其唯一的對象的方式,可以直接通路,不需要執行個體化該類的對象
- 1、單例類隻能有一個執行個體。
- 2、單例類必須自己建立自己的唯一執行個體。
- 3、單例類必須給所有其他對象提供這一執行個體。
就如在單頁面中,全局就隻有一個Audio對象,在需要音頻的地方更換 src ,然後控制其播放,暫停等一系列:
在小程式中,隻有一個背景音頻,然後切換音頻就是更改 src ,然後控制其播放,暫停等一系列 :
// app.js
App({
onLaunch(){
},
audioContext: wx.getBackgroundAudioManager()
})
然後播放頁面中使用即可
// play.js
const app = getApp()
Page({
onLoad(){
},
onShow(){
this.playAudio()
},
playAudio(){
let audioDom = app.audioContext
audioDom.title = '起風了'
audioDom.src = 'http://audio.22family.com/audio/url/1553691835029489897'
audioDom.coverImgUrl = 'http://audio.22family.com/audio/cover/1553691837836859778'
audioDom.play()
}
})
工廠模式
這種類型的設計模式屬于建立型模式,它提供了一種建立對象的最佳方式。在工廠模式中,我們在建立對象時不會對用戶端暴露建立邏輯,并且是通過使用一個共同的接口來指向新建立的對象
就如express就是是工廠模式:
function express(){
var express = {}, req = {}, res = {};
express.get = function(pathName, cb){
// 根據nodejs的path子產品擷取現在的路由
// if(path=== pathName){ cb(req, res) }
cb(req, res)
}
res.send = function(msg){
document.write(msg)
}
return express;
}
var app = express()
app.get('/', function(req, res){
res.send(111)
})
觀察者模式
觀察者模式( 又叫釋出者-訂閱者模式 )應該是最常用的模式之一. 在很多語言裡都得到大量應用. 包括我們平時接觸的dom事件. 也是js和dom之間實作的一種觀察者模式.
- 就像常見的監聽
window.addEventListener('click', function(){
console.log('正在點選')
})
- 在vue中的watch和computed:
vue中watch和computed就相當于訂閱了data的資料,當資料變化是通知到它們,然後執行相應的操作;vue的虛拟Dom樹也訂閱了data,computed,watch的資料,當其變化時通知Dom樹渲染;低版本的vue可能複雜的資料結構資料變化監聽不到,要是用set來設定資料,或者使用$forceUpdate()來強制渲染一次
export default{
data(){
return{
name: 'mySkey'
}
},
computed:{
nameLen(){
return this.name.length
}
},
watch:{
status(){
return this.name === 'mySkey'
}
}
}
- 直播和訂閱
現在火熱的直播,女主播叫點個訂閱,點了訂閱就能收到飛機票了,你點了訂閱就能下次給你發推送了!
- 微信公衆号關注
公衆号能給關注的使用者發推送,也是這種設計模式
原型模式
原型模式(Prototype Pattern)是用于建立重複的對象,同時又能保證性能。這種類型的設計模式屬于建立型模式,它提供了一種建立對象的最佳方式。
這種模式是實作了一個原型接口,該接口用于建立目前對象的克隆。當直接建立對象的代價比較大時,則采用這種模式。例如,一個對象需要在一個高代價的資料庫操作之後被建立。我們可以緩存該對象,在下一個請求時傳回它的克隆,在需要的時候更新資料庫,以此來減少資料庫調用。
就像我們經常使用的js對象,Array,String······
// 這樣定義一個數組
var arr = [1,2,3]
// 其實是執行了下面這樣
var arr = new Array(1,2,3)
然後,arr便繼承了Array的原型屬性和方法(比如length屬性,slice,pop,push,shift,unshift,splice等方法)
同樣的我們也能自己來擴充js的原生對象的原型鍊,比如String沒有reverse方法:
if(!String.prototype.reverse){
String.prototype.reverse = function(){
return Array.prototype.slice.call(this.split('')).reverse().join('')
}
}
var str = 'hello world'
console.log(str.reverse())
js面向對象繼承構造函數的屬性和方法,就是通過js的_proto_來傳遞的
function Person(name, age){
this.name = name
this.age = age
}
// 在prototype中定義的屬性和方法,不會在每次執行個體化的時候都初始化一次,是以把這類必定相同的屬性和方法寫在prototype中
Person.prototype = {
sing: function(){
console.log('sing')
}
}
var mySkey = new Person('mySkey', 24)
mySkey.sing()
console.log(mySkey.name, mySkey.age)
在es6中使用類class,這些屬性和方法還能被繼承。但是在js中原理都一個,它們通過一條原型鍊傳遞
class Person{
constructor(name,age){
this.name = name
this.age = age
}
sing(){
console.log('sing')
}
}
var mySkey = new Person('mySkey', 24)
mySkey.sing()
console.log(mySkey.name, mySkey.age)
擴充卡模式
擴充卡模式(Adapter Pattern)是作為兩個不相容的接口之間的橋梁。這種類型的設計模式屬于結構型模式,它結合了兩個獨立接口的功能。就像我們手機隻能使用20v的電壓,為了在家用電充電,我們需要一個充電頭,它就能将220v的電壓轉成20v的,而且每個廠家的充電頭還不一樣,是以原裝的還貴
在開發中使用到擴充卡模式的情況也多不勝數:
- 1、資料庫設計管理者類型type(甚至其他字段),狀态status,一般就不會使用vachar類型,選擇int類型更加節省空間,就将status定義為 0 禁用 1 可用
在頁面中顯示時,我們肯定不是顯示數字的
通過通路數組的鍵來得到值,這就是擴充卡模式
- 2、通常我們需要對日期,金錢這些資料進行格式化操作
let getYear = (t)=>{
let date = new Date(t)
return date.getFullYear()
}
橋接模式
橋接(Bridge)是用于把抽象化與實作化解耦,使得二者可以獨立變化。這種類型的設計模式屬于結構型模式,它通過提供抽象化和實作化之間的橋接結構,來實作二者的解耦。
- 1、舉一個簡單的例子
我之前不明白為什麼公司代碼裡要将ui架構的toast,confirm,alert這些二次封裝,然後來調用。你試想一下,如果有一天,你需要更換個ui框呢?你之前直接使用ui架構的寫法,是不是就全部得重寫,這是很恐怖的,而你全部寫在一個檔案中的話,隻用更改這個檔案的寫法,這就是橋接模式。
- 2、在實作api的時候,橋接模式就很好用
将請求接口的工具二次封裝,友善下次更換
import axios from 'axios'
let API_URL = 'https://www.baidu.com'
export default{
get (url, data){
return new Promise((resolve,reject)=>{
axios({
url: API_URL + url,
method: 'get',
params: data,
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}
}).then((res) => {
if(res.data.code == 404){
alert('系統似乎出了點問題~');
return reject(res.data);
}else{
return resolve(res.data);
}
});
}).catch((err)=>{
reject(err)
})
}
}
過濾器模式
過濾器模式(Filter Pattern)或标準模式(Criteria Pattern)是一種設計模式,這種模式允許開發人員使用不同的标準來過濾一組對象,通過邏輯運算以解耦的方式把它們連接配接起來。這種類型的設計模式屬于結構型模式,它結合多個标準來獲得單一标準
- 1、購物車訂單
購物車訂單送出的時候,我們隻需要送出被使用者選擇的商品的id
sunmit(orders){
let idsArr = orders.filter(v=> v.isSelected)
let ids = idsArr.join('')
ajax.post('/order', { ids })
}
- 2、去除關鍵詞
經常遇到一些噴子(性情暴躁,語言粗魯),系統要将這些罵人的話屏蔽掉
let filterWords = (words)=>{
let reg = /(cao|sb|你妹|日你媽|狗日的|傻狗)/gi
if(words.match(reg)){
let arr = words.match(reg)
arr.forEach(e=>{
words = words.replace(e, e.split('').map(v=>'*').join(''))
})
}
return words
}
console.log(filterWords('你好, 傻狗'))
裝飾器模式
裝飾器模式(Decorator Pattern)允許向一個現有的對象添加新的功能,同時又不改變其結構。這種類型的設計模式屬于結構型模式,它是作為現有的類的一個包裝。這種模式建立了一個裝飾類,用來包裝原有的類,并在保持類方法簽名完整性的前提下,提供了額外的功能
- 就像我們使用形容詞一樣
對類進行修飾,在執行個體化的時候加上形容詞;知道中間件的人最能将這兩者聯系起來,其實裝飾器就和中間件差不多
function war(status){
return function (target){
target.prototype.wear = '穿着' + status
target.prototype.sing = function(){
console.log('sing')
}
}
}
@war('樸素的')
class Father{}
@war('時髦的')
class Daughter{}
var father = new Father()
var daughter = new Daughter()
console.log(father.wear + '父親, ' + daughter.wear + '女兒')
- 了解成中間件
可将一些日志操作及其他放置在裝飾器中,在類的聲明時就能記錄相應的日志
function method(){
console.log('使用了函數方法')
}
@method
class Method{} // 使用了函數方法
外觀模式
外觀模式(Facade Pattern)隐藏系統的複雜性,并向用戶端提供了一個用戶端可以通路系統的接口。這種類型的設計模式屬于結構型模式,它向現有的系統添加一個接口,來隐藏系統的複雜性
就像你需要擷取一個使用者的姓名和他的年齡,有兩個函數分别執行這兩個功能
let getName = ()=>'mySkey'
let getAge = ()=>23
那麼你需要分别調用兩個函數才能一次擷取姓名和年齡,為何不提供一個進階的接口呢?
let getUserinfo = ()=>{
return getNum() + getAge()
}
享元模式
享元模式(Flyweight Pattern)主要用于減少建立對象的數量,以減少記憶體占用和提高性能。這種類型的設計模式屬于結構型模式,它提供了減少對象數量進而改善應用所需的對象結構的方式
- 時間顯示控件
示範一下未使用享元模式的程式,這個代碼就會多次執行個體化Date對象,雖然很小,但是随着時間推移,會使浏覽器的記憶體越來越少,最終肯定會挂掉
let getCurrentTime = ()=>{
let date = new Date()
let year = date.getFullYear()
let month = date.getMonth() + 1
let day = date.getDate()
let hour = date.getHours()
let minute = date.getMinutes()
let second = date.getSeconds()
let dateArr = [year, month, day, hour, minute, second]
dateArr = dateArr.map((v,k)=>{
if(k!=0) v=v.toString().padStart(2, '0')
return v
})
return dateArr.join('-')
}
setInterval(() => {
console.log(getCurrentTime())
}, 1000)
是以使用享元模式來重改這段代碼,隻執行個體化一次Date對象,就不會造成多次執行個體化,占用大量記憶體空間了
let date = new Date()
let timestamp = Date.parse(date)
let getCurrentTime = ()=>{
timestamp += 1000
date.setTime(timestamp)
let year = date.getFullYear()
let month = date.getMonth() + 1
let day = date.getDate()
let hour = date.getHours()
let minute = date.getMinutes()
let second = date.getSeconds()
let dateArr = [year, month, day, hour, minute, second]
dateArr = dateArr.map((v,k)=>{
if(k!=0) v=v.toString().padStart(2, '0')
return v
})
return dateArr.join('-')
}
setInterval(() => {
console.log(getCurrentTime())
}, 1000)
代理模式
在代理模式(Proxy Pattern)中,一個類代表另一個類的功能。這種類型的設計模式屬于結構型模式
就像我們搬家,以前隻能自己搬,掌握了代理模式之後就能交給搬家公司來搬,這樣就不用每家每戶都是自己來搬家了
class Home{
constructor(name){
this.name = name
}
move(){
console.log('自己搬家')
}
}
class moveCompany{
superMove(user){
console.log('專業搬家三十年,接單客戶:' + user)
}
}
let myHome = new Home('mySkey')
let company = new moveCompany()
myHome.move()
company.superMove(myHome.name)
- ajax請求
調用ajax請求的時候,無論是各種開源庫,還是自己寫的Ajax類, 都會給xhr對象設定一個代理. 我們不可能頻繁的去操作xhr對象發請求, 而應該是這樣
var request = Ajax.get( 'cgi.xx.com/xxx' );
request.send();
request.done(function(){
});
職責鍊模式
責任鍊模式(Chain of Responsibility Pattern)為請求建立了一個接收者對象的鍊。這種模式給予請求的類型,對請求的發送者和接收者進行解耦。這種類型的設計模式屬于行為型模式
- 産品的流程
就舉個例子,網際網路産品到底經曆了什麼?首先是一個需求,有需求才是市場嘛!客戶用一個功能需求,他便聯系了一個網際網路公司,提了需求,公司這邊報了價,客戶同意了報價。由(售前)從業人員和客戶簽訂合同,然後把需求報給(産品經理),産品經理設計出基本互動原型,然後把需求傳遞給(UI)設計師,然後調整多次開UI評審會,通過後交給(程式員)開發人員開發
let makeProduction = ()=>{
salerBefore()
productManager()
uiDesign()
if('ui評審會通過'){
programmer()
}
}
let salerBefore = ()=>console.log('接單簽合同')
let productManager = ()=>console.log('确定需求繪制原型')
let uiDesign = ()=>console.log('設計優化')
let programmer = ()=>console.log('寫BUG')
makeProduction('商城')
指令模式
指令模式(Command Pattern)是一種資料驅動的設計模式,它屬于行為型模式。請求以指令的形式包裹在對象中,并傳給調用對象。調用對象尋找可以處理該指令的合适的對象,并把該指令傳給相應的對象,該對象執行指令
- redux的reducer
使用過react和redux的人一定寫過reducer,外部不需要了解到底需要操作什麼,隻根據action的type來執行響應操作
let defaultData = [
{ audios: [], page: {} }, // 推薦
{ audios: [], page: {} }, // 最熱
{ audios: [], page: {} }, // 原創
{ audios: [], page: {} }, // 飙升
{ audios: [], page: {} }, // 最新
]
let musics = (state, action) => {
if (typeof state === 'undefined') {
return defaultData
}
switch (action.type) {
case 'setMusics':
return action.musics
case 'saveMusics':
let saveMusics = [].concat(state)
saveMusics[action.musics.type] = {
audios: saveMusics[action.musics.type].audios.concat(action.musics.audios),
page: action.musics.page
}
return saveMusics
default:
return state
}
}
export { musics }
- npm和yarn這種包管理工具
包管理工具也是通過指令來區分操作
解釋器模式
解釋器模式(Interpreter Pattern)提供了評估語言的文法或表達式的方式,它屬于行為型模式。這種模式實作了一個表達式接口,該接口解釋一個特定的上下文。這種模式被用在 SQL 解析、符号處理引擎等
每門語言都有解釋器的,就像憲法規定什麼行為合法,什麼不合法一樣!
疊代器模式
疊代器模式(Iterator Pattern),這種模式用于順序通路集合對象的元素,不需要知道集合對象的底層表示
- 1、數組疊代器
let forEach = (arr, cb)=>{
for(let i=0;i<arr.length;i++){
cb(arr[i], i)
}
}
forEach(['hi','he','she'],(v,i)=>console.log(v,i))
- 2、對象疊代器
let forEach = (obj, cb)=>{
for(let i in obj){
cb(obj[i], i)
}
}
forEach({ name: 'mySkey', age: '23' },(v,i)=>console.log(v,i))
中介者模式
中介者模式(Mediator Pattern)是用來降低多個對象和類之間的通信複雜性。這種模式提供了一個中介類,該類通常處理不同類之間的通信,并支援松耦合,使代碼易于維護。中介者模式屬于行為型模式
就像房屋中介,房主将自己房子挂在他們那裡,然後你去找中介的時候,他們就把帶你去看他們手上的房源;就像淘寶網,淘寶商家釋出了商品,你發起訂單,付款到淘寶,商家發貨,你确認收貨後,淘寶将錢打入商家賬戶
備忘錄模式
備忘錄模式(Memento Pattern)儲存一個對象的某個狀态,以便在适當的時候恢複對象。備忘錄模式屬于行為型模式。
- 1、vue中的keep-alive就能緩存資料
- 2、小程式能緩存資料
- 3、無限下拉重新整理
let cache = {}
onReachBottom(){
page += 1 // 頁碼加 1
ajax.get('/list', { page }).then(res=>{
cache[page] = res.data.list // 将每頁資料緩存起來,減少伺服器開銷
this.list = this.list.concat(res.data.list)
})
}
狀态模式
在狀态模式(State Pattern)中,類的行為是基于它的狀态改變的。這種類型的設計模式屬于行為型模式
還是拿分頁來舉個例子,如果要更換下查詢條件,但是現在分頁的頁碼已經大于1了,但是我們明顯需要傳入的頁碼是1,那麼就可以改變一下擷取資料的函數
let getList = (clear = false)=>{
if(clear){ // 需要重置頁碼的地方調用時傳入個 true
page = 1
}
ajax.get('/list', { page }).then(res=>{
this.list = res.data.list
})
}
政策模式
在政策模式(Strategy Pattern)中,一個類的行為或其算法可以在運作時更改。這種類型的設計模式屬于行為型模式。在政策模式中,我們建立表示各種政策的對象和一個行為随着政策對象改變而改變的 context 對象。政策對象改變 context 對象的執行算法
其實就是定義一系列的算法,把它們一個個封裝起來,并且使它們可互相替換
- 就像驗證送出資料的正确性,可以将校驗規則單獨封裝起來,集中管理,之後判斷傳回的是true還是false
export default {
email(email){
let reg = /^\[email protected][a-zA-Z0-9]{2,10}(?:\.[a-z]{2,4}){1,3}$/
return reg.test(email)
},
phone(phone){
let reg = /^((0\d{2,3}-\d{7,8})|(1[3584]\d{9}))$/
return reg.test(phone)
}
}
然後使用時就能需要的地方引入來校驗
import valid from '/valid.js'
console.log(valid.phone('12345678912'))
模闆模式
在模闆模式(Template Pattern)中,一個抽象類公開定義了執行它的方法的方式/模闆。它的子類可以按需要重寫方法實作,但調用将以抽象類中定義的方式進行。這種類型的設計模式屬于行為型模式
就像我們的元件一樣,哪裡需要哪裡搬,我們就是搬磚的,其實就是相當于把代碼copy了一份過來
- 各種遊戲都有初始化,開始,結束流程
class Game{
//初始化遊戲
initialize(){}
//開始遊戲
startPlay(){}
//結束遊戲
endPlay(){}
play(){
this.initialize();
this.startPlay();
this.endPlay();
}
}
class Chess extends Game{
initialize(){
console.log('初始化棋牌遊戲')
}
startPlay(){
console.log('開始棋牌遊戲')
}
endPlay(){
console.log('結束棋牌遊戲')
}
}
var game = new Chess()
game.play()
通路者模式
在通路者模式(Visitor Pattern)中,我們使用了一個通路者類,它改變了元素類的執行算法。通過這種方式,元素的執行算法可以随着通路者改變而改變。這種類型的設計模式屬于行為型模式。根據模式,元素對象已接受通路者對象,這樣通路者對象就可以處理元素對象上的操作
- 就像一個函數是相同的,但是調用的對象不一樣導緻結果不同
function showName(){
console.log(this.name)
}
window.name = 'hello world'
let mySkey = { name: 'mySkey' }
let xiaoming = { name: '小明' }
showName()
showName.call(mySkey)
showName.call(xiaoming)
組合模式
組合模式又叫部分-整體模式,它将所有對象組合成樹形結構。使得使用者隻需要操作最上層的接口,就可以對所有成員做相同的操作
- 經常我們需要隐藏整個闆塊,那麼隐藏父級容器就能達到效果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>combination</title>
</head>
<body>
<ul id="container">
<li></li>
<li></li>
<li></li>
</ul>
<script type="text/javascript">
var dom = document.getElementById('container')
dom.style.display = 'none'
</script>
</body>
</html>
- 檔案夾,我們删除檔案夾也會删除下面的檔案夾及檔案,給檔案夾添權重限也會影響到下面的
mixin模式
mixin模式就是一些提供能夠被一個或者一組子類簡單繼承功能的類,意在重用其功能。在面向對象的語言中,我們會通過接口繼承的方式來實作功能的複用。但是在javascript中,我們沒辦法通過接口繼承的方式,但是我們可以通過javascript特有的原型鍊屬性,将功能引用複制到原型鍊上,達到功能的注入。
其實就是一個包括了混入對象所有的屬性方法的超集,最簡單的一個例子:
let a = { a: 'A' }
let b = { b: 'B' }
let c = { ...a, ...b }
console.log(c)
一個比較健壯的mixin模式
function extend(destClass) {
var classes = Array.prototype.slice.call(arguments, 1);
for (var i=0; i<classes.length; i++) {
var srcClass = classes[i];
var srcProto = srcClass.prototype;
var destProto = destClass.prototype;
for (var method in srcProto) {
if (!destProto[method]) {
destProto[method] = srcProto[method];
}
}
}
}
function Book() {}
Book.prototype.getName = function() {
console.log('Book getName')
}
Book.prototype.setName = function() {
console.log('Book setName')
}
function Tech(){}
Tech.prototype.showTech = function(){
console.log('Tech showTech')
}
function JS() {}
extend(JS, Book, Tech);
var js = new JS();
js.getName()
MVC模式
MVC 是一種使用 MVC(Model View Controller 模型-視圖-控制器)設計建立 Web 應用程式的模式:
- Model(模型)
表示應用程式核心(比如資料庫記錄清單),是應用程式中用于處理應用程式資料邏輯的部分。通常模型對象負責在資料庫中存取資料
- View(視圖)
顯示資料(資料庫記錄),是應用程式中處理資料顯示的部分。通常視圖是依據模型資料建立的
- Controller(控制器)
處理輸入(寫入資料庫記錄),是應用程式中處理使用者互動的部分。通常控制器負責從視圖讀取資料,控制使用者輸入,并向模型發送資料
- MVC優點:
1、耦合性低,視圖層和業務層分離,這樣就允許更改視圖層代碼而不用重新編譯模型和控制器代碼。
2、重用性高
3、生命周期成本低
4、MVC使開發和維護使用者接口的技術含量降低
5、可維護性高,分離視圖層和業務邏輯層也使得WEB應用更易于維護和修改
6、部署快
- MVC缺點:
1、不适合小型,中等規模的應用程式,花費大量時間将MVC應用到規模并不是很大的應用程式通常會得不償失。
2、視圖與控制器間過于緊密連接配接,視圖與控制器是互相分離,但卻是聯系緊密的部件,視圖沒有控制器的存在,其應用是很有限的,反之亦然,這樣就妨礙了他們的獨立重用。
3、視圖對模型資料的低效率通路,依據模型操作接口的不同,視圖可能需要多次調用才能獲得足夠的顯示資料。對未變化資料的不必要的頻繁通路,也将損害操作性能
- 應用:
在web app 流行之初, MVC 就應用在了java(struts2)和C#(ASP.NET)服務端應用中,後來在用戶端應用程式中,基于MVC模式,AngularJS應運而生
MVP模式
- Model(模型)
表示應用程式核心(比如資料庫記錄清單),是應用程式中用于處理應用程式資料邏輯的部分。通常模型對象負責在資料庫中存取資料
- View(視圖)
顯示資料(資料庫記錄),是應用程式中處理資料顯示的部分。通常視圖是依據模型資料建立的
- Presenter (釋出者)
在View和Model之間起到橋梁的作用,又是一個獨立的大子產品,封裝了業務的複雜度,将UI和業務邏輯拆分開來,使UI和業務都可以獨立的進行變化。從整體的資料流向上看,Presenter從Model層擷取資料,并通過接口發送給View層展示;View層将使用者互動傳遞給Presenter,由Presenter完成相應的業務邏輯,這其中可能會有Model層的參與,比如Presenter調用Model層的接口來儲存資料。
- MVP優點:
1、模型與視圖完全分離,我們可以修改視圖而不影響模型
2、可以更高效地使用模型,因為所有的互動都發生在一個地方——Presenter内部
3、我們可以将一個Presenter用于多個視圖,而不需要改變Presenter的邏輯。這個特性非常的有用,因為視圖的變化總是比模型的變化頻繁
4、如果我們把邏輯放在Presenter中,那麼我們就可以脫離使用者接口來測試這些邏輯(單元測試)
- MVP缺點:
視圖和Presenter的互動會過于頻繁,使得他們的聯系過于緊密。也就是說,一旦視圖變更了,presenter也要變更
- MVP應用:
可應用與Android開發
MVVM模式
WPF的資料綁定與Presentation Model相結合是非常好的做法,使得開發人員可以将View和邏輯分離出來,但這種資料綁定技術非常簡單實用,也是WPF所特有的,是以我們又稱之為Model-View-ViewModel(MVVM)
- Model(模型)
表示應用程式核心(比如資料庫記錄清單),是應用程式中用于處理應用程式資料邏輯的部分。通常模型對象負責在資料庫中存取資料
- View(視圖)
顯示資料(資料庫記錄),是應用程式中處理資料顯示的部分。通常視圖是依據模型資料建立的
- viewModel(視圖控制)
View 和 Model 之間并沒有直接的聯系,而是通過ViewModel進行互動,ViewModel 通過雙向資料綁定把 View 層和 Model 層連接配接了起來,而View 和 Model 之間的同步工作完全是自動的,無需人為幹涉,是以開發者隻需關注業務邏輯,不需要手動操作DOM, 不需要關注資料狀态的同步問題,複雜的資料狀态維護完全由 MVVM 來統一管理
- MVVM優點:
1、低耦合。視圖(View)可以獨立于Model變化和修改,一個ViewModel可以綁定到不同的"View"上,當View變化的時候Model可以不變,當Model變化的時候View也可以不變。
2、可重用性。你可以把一些視圖邏輯放在一個ViewModel裡面,讓很多view重用這段視圖邏輯。
3、獨立開發。開發人員可以專注于業務邏輯和資料的開發(ViewModel),設計人員可以專注于頁面設計,使用Expression Blend可以很容易設計界面并生成xaml代碼。
4、可測試。界面素來是比較難于測試的,而現在測試可以針對ViewModel來寫
- 應用:
vue開發,小程式
參考文獻
設計模式導圖
菜鳥教程設計模式
前端設計模式
MVC、MVP和MVVM的差別
《Javascript設計模式》