模型,视图和控制器之间的关系让我感到困惑。
此主题显示箭头表单视图到控制器,从控制器到模型的箭头和从模型到视图的箭头: http://www.codeproject.com/Tips/31292/MVC-v-s-MVP-How-Common-and-How-Different
但是,本主题显示了Model和View之间的双箭头; View和Controller之间的双箭头;和从控制器到模型的箭头: http://www.codeproject.com/Articles/288928/Differences-between-MVC-and-MVP-for-Beginners
最后,本主题显示了从视图到模型的箭头,从控制器到模型的箭头以及从控制器到视图的箭头: http://www.w3schools.com/aspnet/mvc_intro.asp
我有一些问题:
答案 0 :(得分:7)
我发现你所链接的所有图片都令人困惑。这张图片(taken from Wikipedia)最好地解释了它。
如何运作
MVC考虑三个角色。 模型是表示域的一些信息的对象。这是一个非视觉的 包含除用于之外的所有数据和行为的对象 UI。
视图表示UI中模型的显示。因此,如果 我们的模型是一个客户对象,我们的视图可能是一个充满UI的框架 窗口小部件或使用模型信息呈现的HTML页面。该 视图只是关于信息的显示;任何改变 信息由MVC三位一体的第三个成员处理: 控制器。 控制器获取用户输入,操作模型,并使视图适当更新。这样UI就是 视图和控制器的组合。
- 由Martin Fowler引自Patterns of Enterprise Application Architecture
通常,视图从模型中获取必要的信息。使用被动视图时,从控制器传递对象(来自模型)。重要的是视图只从模型中读取并且从不写入/更新它。
视图观察并响应模型中的更改。该模型是Domain Model而不是单个记录集或实体。
目前普遍使用的MVC与原始的MVC模式不同,因为它是Martin Fowler创造的。他将此模式基于 Smalltalk 。
MVC的核心,以及对后来的框架影响最大的想法,就是我所说的Separated Presentation。
MVC的另一部分是模型,视图和控制器的交互方式。
在这种情况下,所有视图和控制器都会观察模型。当模型发生变化时,视图会做出反应。
这与MVC非常不同,因为 Ruby on Rails 受欢迎,其中控制器负责准备和加载视图。
class ArticlesController < ApplicationController
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article
else
render 'new'
end
end
Martin Fowler将MVC缩减为此
- 在演示文稿(视图和控制器)和域(模型)之间进行强有力的分离 - 分离演示文稿。
- 将GUI小部件划分为控制器(用于响应用户激励)和视图(用于显示模型的状态)。控制器和视图 应该(大部分)不直接沟通,而是通过模型。
- 让视图(和控制器)观察模型以允许更新多个小部件而无需直接通信 - 观察者 同步。
- 由Martin Fowler引自GUI Architectures
答案 1 :(得分:3)
维基百科的MVC形象存在很多混乱。问题在于,在UML中A->B
意味着A是活动的并且调用B.但是wikipedists社区不是纯粹的技术,并且用其他令人困惑的箭头意义绘制图像。有一整圈箭头看起来很合理,不是吗?
不。特别是Model --updates--> View
关系令人厌恶。从技术上讲,它是View --reads--> Model
。如果模型处于活动状态,则意味着数据对象和操作依赖于系统的其他部分,因此它不可重用。
另一个废话是User --uses--> Controller
。控制器是隐形的,用户只使用View,其他部分都是黑盒子。 Controller基本上是事件实现系统。事件的来源可以是用户的输入或模型的数据更改,但他们使用接口,它是实现它们的控制器。 (它被称为控制反转,因此有些人会混淆地向相反方向绘制箭头。)这些动作命令模型和视图更新,因此箭头指向控制器。没有什么可以控制Controller。这就是为什么它被称为控制器:所有控制都聚合到它。 Active View是一个例外:如果需要填充其selectbox,它可以在没有Controller祝福的情况下读取模型。但有时View不依赖于Model的接口,因此并非所有的Views都被设计为活动的。
所以正确的图像是
--- Controller ----
| |
V V
View ------------> Model
(if active)
或者,如果您想在图像中合并User,从系统的角度来看,它是一个事件源,不一定是唯一的一个:
Other event src <---
|
|
-- Controller -----
| |
V V
User <--> View ------------> Model
(if active)
有时,模型也可能是事件的来源,即如果某些帐户信用额度降至某个水平以下,则可能会发出视图信号以显示警告。但是模型应该独立于系统的其他部分,而Observer设计模式是实现它的方式,请参见简化示例:
<强>模型强>
模型使用接口让View挂钩到“帐户太低”或“帐户太高”的事件
interface AccountObserver {
// in dummy examples, these methods are often vaguely named update()
public void accountLow(int value);
public void accountHigh(int value);
}
class Model {
// protected, not private, to make the Model extensible
protected int account;
protected AccountObserver observer;
// more observers should be allowed, we should have array of observers
// and name the method "register..." instead of "set..."
public void setAccountObserver(AccountObserver o) {
observer = o;
}
public void updateAccount(int change) {
account+= change;
// calculate values ...
if(account<minValue) o.accountLow(account);
if(account>maxValue) o.accountHigh(account);
}
...
}
查看强>
有些人会建议聚合Observer而不是实现它。继承更简单,如果模型以其方法具有唯一名称的方式定义所有观察者,我们就可以继承。
class View : AccountObserver {
public void accountLow(int value) {
warning("Account too low! It has only "+value+" credits!");
}
public void accountHigh(int value) {
warning("Account too high! It has above "+value+" credits!");
}
...
}
<强>控制器强>
在架构的Controller部分,我们将用户界面(View)和其他事件源与Model(可能包含多个数据源)组合在一起。在我们最简单的情况下:
class Controller {
protected Model model;
protected View view;
public Controller() { // Constructor
model.setAccountObserver(view);
}
void mainLoop() {
// observe input sources ...
if(int value=somethingUpdatesAccount()) {
model.updateAccount(value); // affects the View here
}
}
}
注意model.setAccountObserver(view)
- 模型和视图对象(作为Controller的属性)是耦合的,但模型和视图类是独立的。这种依赖注入模式是理解模型 - 视图关系的关键。