MVC pattern. The relationship between Model, View

2019-06-20 19:57发布

问题:

The relationship between Model, View and Controller make me confused.

This topic shows an arrow form View to Controller, an arrow from Controller to Model and an arrow from Model to View: http://www.codeproject.com/Tips/31292/MVC-v-s-MVP-How-Common-and-How-Different

However, this topic shows a dual-arrow between Model and View; a dual-arrow between View and Controller ; and an arrow from Controller to Model: http://www.codeproject.com/Articles/288928/Differences-between-MVC-and-MVP-for-Beginners

Finally, this topic shows an arrow from View to Model, an arrow from Controller to Model and an arrow from Controller to View: http://www.w3schools.com/aspnet/mvc_intro.asp

I have some questions:

  1. Which relationships are correct?
  2. Business Logic should be handled in Controller or Model? I've read somewhere that the business logic shouldn't be placed in Controller (ASP.Net MVC)
  3. In case the controller pass an object to a view, is this object belonged to Model?
  4. How does the view retrieve data directly from model? Does it have the reference directly to model or it interact with the model coming from Controller?

回答1:

I find all of the images you're linking to confusing. This image (taken from Wikipedia) explaines it best.

How It Works

MVC considers three roles. The model is an object that represents some information about the domain. It's a nonvisual object containing all the data and behavior other than used for the UI.

The view respresents the display of the model in the UI. Thus, if our model is a customer object our view might be a frame full of UI widget or an HTML page rendered with information from the model. The view is only about display of information; any changes to the information are handled by the third member of the MVC trinity: the controller. The controller takes user input, manipulates the model, and causes the view to update appropriately. In this way UI is a combination of the view and the controller.

-- Quoted from Patterns of Enterprise Application Architecture by Martin Fowler

Your questions

  1. MVC is about separation of concerns, not about relationships.
  2. Business Logic should be in the model. The controller is only for interacting with the user.
  3. Yes (most likely)
  4. Typically the view fetches the necessary information from the model. When using passive views, objects (from the model) are passed from the controller. Important is that the view only reads from the model and never writes/updates it.

    The View observes and responds to changes in model. The model is the Domain Model rather than an individual recordset or entity.

Errata

How MVC is commonly used in the present time differs from the original MVC pattern as it was coined by Martin Fowler. He based this pattern in Smalltalk.

At the heart of MVC, and the idea that was the most influential to later frameworks, is what I call Separated Presentation.

Another part of MVC is how the model, view and controller interact.

In this case all the views and controllers observe the model. When the model changes, the views react.

This is very different from MVC as made popular by Ruby on Rails, where the controller is responsible for preparing and loading the view.

class ArticlesController < ApplicationController
  def create
    @article = Article.new(article_params)

    if @article.save
      redirect_to @article
    else
      render 'new'
    end
  end

Martin Fowler condenses MVC down to this

  • Make a strong separation between presentation (view & controller) and domain (model) - Separated Presentation.
  • Divide GUI widgets into a controller (for reacting to user stimulus) and view (for displaying the state of the model). Controller and view should (mostly) not communicate directly but through the model.
  • Have views (and controllers) observe the model to allow multiple widgets to update without needed to communicate directly - Observer Synchronization.

-- Quoted from GUI Architectures by Martin Fowler



回答2:

There are lots of confusion with the Wikipedia's MVC image. The issue is that in UML A->B means that A is active and calls B. But wikipedists community is not pure technical and draws images with other confusing meanings of arrows. It looks nice and reasonable to have a full circle of arrows, no?

No. Especially the Model --updates--> View relation is abomination. Technically it is View --reads--> Model. If the Model were active, it would mean that the data objects and actions are dependent on other parts of the system, so it would not be reusable.

The other nonsense is the User --uses--> Controller. Controller is invisible, users use only View, other parts are blackbox to them. The Controller is basically system of events implementations. The source of the events can be User's input or Model's data change, but they uses interfaces and it is the Controller which implements them. (It is called Inversion of Control and because of it some people confusingly draws the arrows in opposite direction.) These actions command Model and View to update, therefore the arrows point from the Controller. Nothing controls Controller. That's why it is called Controller: all control is aggregated into it. Active View is an exception: it can read Model without Controller's blessing if it needs to fill its selectbox for instance. But sometimes View's dependence on Model's interface is undesirable, so not all Views are designed active.

So the correct image is

  --- Controller ----
  |                 |
  V                 V
View ------------> Model
     (if active)

Or, if you want to incorporate User in the image, from the system's perspective it is an event source, not necessarily the only one:

Other event src <---
                   |
                   |
            -- Controller -----
            |                 |
            V                 V
User <--> View ------------> Model
                (if active)

Active Model scenario

Sometimes the Model can be also the source of events, i.e. if some account credit drops below some level, it could signal the View to show warning. But the Model should be independent to other part of the system and Observer design pattern is the way to implement it, see the simplified example:

Model

Model uses interface to let the View hook to its event of "account too low" or "account too high"

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);
    }
    ...
}

View

Some people would recommend to aggregate Observer rather than implement it. The inheritance is simpler and if the model defines all the observers in a way that their methods have unique names, we can inherit.

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

In the Controller part of the architecture we put together the user interface (View) and other event sources with Model (which may consist of more than one data sources). In our simplest case:

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
        }
    }
}

Note the model.setAccountObserver(view) - the model and view objects (as properties of the Controller) are coupled, but the Model and View classes are independent. This Dependency Injection pattern is the key to understand the Model - View relation.

Now to your questions

  1. Which relationships are correct? All and none. All, because the differences come from different meaning of the arrows. None, because the meaning of their arrows are not explicitly described (or wrong like Wikipedia's image, se Olexander Papchenko's comment).
  2. Business Logic should be handled in Controller or Model? If your model is robust, it may contain large part of business logic. For instance adding credits to an account may require the knowledge of users privileges, account values and settings. Controller may just call "do the action" and Model can respond what was the result. In this case, the Model has full control of data consistency. It is good if the responsibility is on one place. It may be tempting to move some of the logic to the Controller which doesn't do much in this case while the Model grows large. I would recommend it only if you have only one application that works with the Model, because if the Controller contains bug and stores wrong data, you often don't know which application was responsible.
  3. In case the Controller pass an object to the View, is this object belonged to Model? Yes in MVP. In MVC, the View can also register its Model(s) (outside of the Controller) and call the Model's methods directly.
  4. How does the view retrieve data directly from model? Does it have the reference directly to model or it interact with the model coming from Controller? I fully agree with Arnold Daniels' answer to this question. If the View contains a button like "Update", the data are sent to the Controller which may do other actions with them (like logging, modifying, analyzing, filtering) before passing them to the Model.