MVC模式。模型,视图和控制器之间的关系

时间:2015-03-12 08:47:57

标签: model-view-controller mvp

模型,视图和控制器之间的关系让我感到困惑。

此主题显示箭头表单视图到控制器,从控制器到模型的箭头和从模型到视图的箭头: 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

我有一些问题:

  1. 哪种关系正确?
  2. 业务逻辑应该在Controller或Model中处理?我已经读过某个地方,业务逻辑不应放在Controller(ASP.Net MVC)
  3. 如果控制器将对象传递给视图,该对象是否属于Model?
  4. 视图如何直接从模型中检索数据?它是直接引用模型还是与来自Controller的模型交互?

2 个答案:

答案 0 :(得分:7)

我发现你所链接的所有图片都令人困惑。这张图片(taken from Wikipedia)最好地解释了它。

MVC diagram

  

如何运作

     

MVC考虑三个角色。 模型是表示域的一些信息的对象。这是一个非视觉的   包含除用于之外的所有数据和行为的对象   UI。

     

视图表示UI中模型的显示。因此,如果   我们的模型是一个客户对象,我们的视图可能是一个充满UI的框架   窗口小部件或使用模型信息呈现的HTML页面。该   视图只是关于信息的显示;任何改变   信息由MVC三位一体的第三个成员处理:   控制器。 控制器获取用户输入,操作模型,并使视图适当更新。这样UI就是   视图和控制器的组合。

- 由Martin Fowler引自Patterns of Enterprise Application Architecture

您的问题

  1. MVC是关注点的分离,而不是关系。
  2. 业务逻辑应该在模型中。控制器仅用于与用户交互。
  3. 是(最有可能)
  4. 通常,视图从模型中获取必要的信息。使用被动视图时,从控制器传递对象(来自模型)。重要的是视图只从模型中读取并且从不写入/更新它。

    视图观察并响应模型中的更改。该模型是Domain Model而不是单个记录集或实体。

  5. 勘误表

    目前普遍使用的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的属性)是耦合的​​,但模型和视图是独立的。这种依赖注入模式是理解模型 - 视图关系的关键。

现在回答你的问题

  1. 哪些关系正确?全部且无。 全部,因为差异来自箭头的不同含义。 ,因为他们的箭头的含义没有明确描述(或者像维基百科的形象一样错误,请参阅Olexander Papchenko的评论)。
  2. 应该在Controller或Model中处理业务逻辑?如果您的模型是健壮的,它可能包含大部分业务逻辑。例如,向帐户添加信用可能需要知道用户权限,帐户值和设置。控制器可以只调用“做动作”,模型可以响应结果。在这种情况下,模型可以完全控制数据一致性。如果责任在一个地方是好的。将一些逻辑移到控制器上可能很有吸引力,在这种情况下,当模型变大时,这种逻辑不会做太多。我建议只有当你只有一个适用于Model的应用程序时,因为如果Controller包含bug并存储错误的数据,你通常不知道哪个应用程序负责。
  3. 如果Controller将对象传递给View,此对象是否属于Model?是MVP中的Yes。在MVC中,View也可以注册其Model(s)(在Controller之外)并直接调用Model的方法。
  4. 视图如何直接从模型中检索数据?它是否直接引用模型或与来自Controller的模型进行交互?我完全同意Arnold Daniels对此问题的回答。如果视图包含“更新”之类的按钮,则数据将被发送到Controller,后者可能会对它们执行其他操作(如记录,修改,分析,过滤),然后再将它们传递给模型。