141

实例说明MVC,MVP,MVVM架构 - 小东毛哥

 6 years ago
source link: http://www.cnblogs.com/maoscut/p/7623462.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

实例说明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>
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都只是稍加修改而已。

在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来存储对应关系,一个数组做不到)。

可以看到,在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,通知视图这部分框架会帮你实现,只要实现数据修改的逻辑就好了。

至此,三个架构都讲完了,如果错误,欢迎讨论。

代码可在github上下载,需要node环境。

参考资料:http://www.cnblogs.com/zhouyangla/p/6936455.html


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK