Model View Controller

MVC is a pattern that is useful for creating rich, interactive user interfaces: just the sort of interfaces which are becoming more and more popular on the web. The astute amongst you will have already figured out that the pattern is made up of three major components: model, view, and controller. You can see how information flows between the components in this illustration:

Model View Controller

The preceding diagram shows the relationship between the three components in MVC.

The model contains the state of the program. In many applications this model is contained in some form, in a database. The model may be rehydrated from a persistent store such as the database or it can be transient. Ideally the model is the only mutable part of the pattern. Neither the view nor the controller has any state associated with them.

For a simple login screen the model might look like the following:

class LoginModel{
  UserName: string;
  Password: string;
  RememberMe: bool;
  LoginSuccessful: bool;
  LoginErrorMessage: string;
}

You'll notice that not only do we have fields for the inputs shown to the user but also for the state of the login. This would not be apparent to the user but it is still part of the application state.

The model is usually modeled as a simple container for information. Typically, there are no real functions in the model. It simply contains data fields and may also contain validation. In some implementations of the MVC pattern the model also contains meta-data about the fields such as validation rules.

Note

The Naked Object pattern is a deviation from the typical MVC pattern. It augments the model with extensive business information as well as hits about the display and editing of data. It even contains methods for persisting the model to storage.

The views in the Naked Object pattern are generated from these models automatically. The controller is also automatically generated by examining the model. This centralizes the logic for displaying and manipulating application states and saves the developer from having to write their own views and controllers. So while the view and controller still exist, they are not actual objects but are dynamically created from the model.

Several systems have been successfully deployed using this pattern. Some criticism has emerged around the ability to generate an attractive user interface from just the models as well as how to properly coordinate multiple views.

In a foreword to the PhD thesis, presenting Naked Objects by Reenskaug, he suggests that the naked objects pattern is actually closer to his original vision for MVC than most of the derivations of MVC in the wild.

Updates to the model are communicated to the view whenever the state changes. This is typically done through the use of an observer pattern. The model does not typically know about either the controller or the view. The first is simply the thing telling it to change and the second is only updated through the observer pattern so the model doesn't have direct knowledge of it.

The view does pretty much what you would expect: communicate the model state to a target. I hesitate to suggest that the view must be a visual or graphical representation of the model as frequently the view is being communicated to another computer and may be in the form of XML, JSON, or some other data format. In most cases, especially those related to JavaScript, the view will be a graphical object. In a web context this will typically be HTML which is rendered by the browser. JavaScript is also gaining popularity on phones and on the desktop, so the view could also be a screen on a phone or on the desktop.

The view for the model presented in the preceding paragraph might look like the following figure:

Model View Controller

In cases, where the observer pattern is not used, then the view may poll the model at some interval looking for changes. In this case the view may have to keep a representation of the state itself or at least a version number. It is important that the view not unilaterally update this state without passing the updates to the controller, otherwise the model and the copy in the view will get out of sync.

Finally, the state of the model is updated by the controller. The controller usually contains all the logic and business rules for updating fields on the model. A simple controller for our login page might look like the following code:

class LoginController {
  constructor(model) {
    this.model = model;
  }
  Login(userName, password, rememberMe) {
    this.model.UserName = userName;
    this.model.Password = password;
    this.model.RememberMe = rememberMe;
    if (this.checkPassword(userName, password))
      this.model.LoginSuccessful;
    else {
      this.model.LoginSuccessful = false;
      this.model.LoginErrorMessage = "Incorrect username or password";
    }
  }
};

The controller knows about the existence of the model and is typically aware of the view's existence as well. It coordinates the two of them. A controller may be responsible for initializing more than one view. For instance, a single controller may provide a list view of all the instances of a model as well as a view that simply provides details. In many systems a controller will have create, read, update, and delete (CRUD) operations on it that work over a model. The controller is responsible for choosing the correct view and for wiring up the communication between the model and the view.

When there is a need for a change to the application then the location of the code should be immediately apparent. For example:

Change

Location

Elements don't appear well spaced on the screen, change spacing.

View

No users are able to log in due to a logical error in password validation.

Controller

New field to be added.

All layers

Note

Presentation-Abstraction-Control (PAC) is another pattern that makes use of a triad of components. In this case its goal is to describe a hierarchy of encapsulated triples that more closely match how we think of the world. The control, similar to an MVC controller, passes interactions up in the hierarchy of encapsulated components allowing for information to flow between components. The abstraction is similar to a model but may represent only a few fields that are important for that specific PAC instead of the entire model. Finally, the presentation is effectively the same as a view.

The hierarchical nature of PAC allows for parallel processing of the components, meaning that it can be a powerful tool in today's multiprocessor systems.

You might notice that the last one there requires a change in all layers of the application. These multiple locations for responsibility are something that the Naked Objects pattern attempts to address by dynamically creating views and controllers. The MVC pattern splits code into locations by dividing the code by its role in user interaction. This means that a single data field lives in all the layers as is shown in this picture:

Model View Controller

Some might call this a cross-cutting concern but really it doesn't span a sufficient amount of the application to be called such. Data access and logging are cross-cutting concerns as they are pervasive and difficult to centralize. This pervasion of a field through the different layers is really not a major problem. However, if it is bugging you then you might be an ideal candidate for using the Naked Objects pattern.

Let's step into building some code to represent a MVC in JavaScript.

MVC code

Let's start with a simple scenario for which we can apply MVC. Unfortunately, Westeros has very few computers, likely due to the lack of electricity. Thus applying application structuring patterns using Westeros as an example is difficult. Sadly we'll have to take a step back and talk about an application which controls Westeros. Let's assume it to be a web application and implement the entirety of MVC on the client side.

It is possible to implement MVC by splitting the model, view and controller between client and server. Typically, the controller would sit on the server and provide an API which is known by the view. The model serves as a communication method both to the view which resides on the web browser and to the data store, likely a database of some form. It is also common that the controller be split between the server and the client in cases where some additional control is required on the client.

In our example we would like to create a screen that controls the properties of a castle. Fortunately, you're lucky that this is not a book on designing user interfaces with HTML as I would certainly fail. We'll stick to a picture in place of the HTML:

MVC code

For the most part, the view simply provides a set of controls and data for the end user. In this example the view will need to know how to call the save function on the controller. Let's set that up:

class CreateCastleView {
  constructor(document, controller, model, validationResult) {
    this.document = document;
    this.controller = controller;
    this.model = model;
    this.validationResult = validationResult;
    this.document.getElementById("saveButton").addEventListener("click", () => this.saveCastle());
    this.document.getElementById("castleName").value = model.name;
    this.document.getElementById("description").value = model.description;
    this.document.getElementById("outerWallThickness").value = model.outerWallThickness;
    this.document.getElementById("numberOfTowers").value = model.numberOfTowers;
    this.document.getElementById("moat").value = model.moat;
  }
  saveCastle() {
    var data = {
      name: this.document.getElementById("castleName").value,
      description: this.document.getElementById("description").value,
      outerWallThickness: this.document.getElementById("outerWallThickness").value,
      numberOfTowers: this.document.getElementById("numberOfTowers").value,
      moat: this.document.getElementById("moat").value
    };
    this.controller.saveCastle(data);
  }
}

You'll notice that the constructor for this view contains both a reference to the document and to the controller. The document contains both HTML and styling, provided by CSS. We can get away with not passing in a reference to the document but injecting the document in this fashion allows for easier testability. We'll look at testability more in a later chapter. It also permits reusing the view multiple times on a single page without worrying about the two instances conflicting.

The constructor also contains a reference to the model which is used to add data to fields on the page as needed. Finally, the constructor also references a collection of errors. This allows for validation errors from the controller to be passed back to the view to be handled. We have set the validation result to be a wrapped collection that looks something like the following:

class ValidationResult{
  public IsValid: boolean;
  public Errors: Array<String>;
  public constructor(){
    this.Errors = new Array<String>();
  }
}

The only functionality here is that the button's onclick method is bound to calling save on the controller. Instead of passing in a large number of parameters to the saveCastle function on the controller, we build a lightweight object and pass that in. This makes the code more readable, especially in cases where some of the parameters are optional. No real work is done in the view and all the input goes directly to the controller.

The controller contains the real functionality of the application:

class Controller {
  constructor(document) {
    this.document = document;
  }
  createCastle() {
    this.setView(new CreateCastleView(this.document, this));
  }
  saveCastle(data) {
    var validationResult = this.validate(data);
    if (validationResult.IsValid) {
      //save castle to storage
      this.saveCastleSuccess(data);
    }
    else {
      this.setView(new CreateCastleView(this.document, this, data, validationResult));
    }
  }
  saveCastleSuccess(data) {
    this.setView(new CreateCastleSuccess(this.document, this, data));
  }
  setView(view) {
    //send the view to the browser
  }
  validate(model) {
    var validationResult = new validationResult();
    if (!model.name || model.name === "") {
      validationResult.IsValid = false;
      validationResult.Errors.push("Name is required");
    }
    return;
  }
}

The controller here does a number of things. The first thing is that it has a setView function which instructs the browser to set the given view as the current one. This is likely done through the use of a template. The mechanics of how that works are not important to the pattern so I'll leave that up to your imagination.

Next, the controller implements a validate method. This method checks to make sure that the model is valid. Some validation may be performed on the client, such as testing the format of a postal code, but other validation requires a server trip. If a username must be unique then there is no reasonable way to test that on the client without communicating with the server. In some cases, the validation functionality may exist on the model rather than in the controller.

Methods for setting up various different views are also found in the controller. In this case we have a bit of a workflow with a view for creating a castle then views for both success and failure. The failure case just returns the same view with a collection of validation errors attached to it. The success case returns a whole new view.

The logic to save the model to some sort of persistent storage is also located in the controller. Again the implementation of this is less important than to see that the logic for communicating with the storage system is located in the controller.

The final letter in MVC is the model. In this case, it is a very light weight one:

class Model {
  constructor(name, description, outerWallThickness, numberOfTowers, moat) {
    this.name = name;
    this.description = description;
    this.outerWallThickness = outerWallThickness;
    this.numberOfTowers = numberOfTowers;
    this.moat = moat;
  }
}

As you can see, all it does is keep track of the variables that make up the state of the application.

Concerns are well separated in this pattern allowing for changes to be made with relative ease.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset