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

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

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

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

posted @ 2017-10-03 11:46  小东毛哥  阅读(1697)  评论(3编辑  收藏  举报