Model-View-Controller Software Design
From Real Software Documentation
Contents |
Introduction
There is a well-established way to design a program called the Model-View-Controller Paradigm. You should use this paradigm in any complex program design involving a user interface.
Note that this paradigm is a very broad and high-level guideline. You will need to apply other design techniques to fully flesh-out your designs.
Programs have (up to) three parts
The Model-View-Controller Paradigm (which we will henceforth call MVC) considers programs to have up to three parts:
- The Model, which is the underlying data or system the program is designed to manipulate. In a word processing program, for example, the model provides a representation of the text and formatting of the document; in a strategy game, the model represents what all the units, terrain and other factors in the game are up to; in a RPN[note 1] calculator, the model would be a stack of numbers.
- The View, which is the presentation of information to the user. In Real Studio programs, this will be a graphical user interface. Note that some programs may not have a view (a web or file server program might not have a direct user interface, for example).
- The Controller, which is the means by which the program is controlled. In Real Studio, this will mostly be graphical user interface controls. Potentially, though, the control for a program could come from a wide range of sources: files, other programs on the same computer or a network, for example.
The Advantages of MVC
MVC is a tried and tested high-level structure for any application with a Graphical User Interface. It was originally developed in IBM’s Smalltalk, which was both one of the earliest and most influential object-oriented programming languages, and also one of the earliest examples of a graphical user interface. It has proven its value over more than 30 years since that time.
You can see MVC as a modern, GUI version of the older view that a program consists of input, processing, and output. By cleanly separating these features within your programs, you gain the same advantages we have seen elsewhere: you can develop and debug each in separation, and you can change any of the three with minimal or no changes to the others.
As an example, consider a complex video game. If you employ MVC in developing the application, you will be able to:
- Easily add new control schemes, such as game controllers;
- Add more complex enemy programming without worrying about whether it will display correctly; and
- Improve the graphics or sound for the game without concern for the mechanics of play.
Implementation by Class and Interface
In more practical terms, the classes and interfaces in your application should generally fit cleanly into one of the three parts of the application, and should have simple communication between them.
Note that a particular class will often fulfill roles in two or even three of the major parts of the application. This is fine, as long as the various roles are separated into different interfaces or the class definition itself, and as long as the other classes that communicate with the class in question do so via the relevant interface.
The point is not that the roles must be separated, but that they can easily be separated. If a single class, say a PlayerShip takes care of reading the keyboard, interacting with the model to determine what is going on in the game world, and displays itself on the screen, that is fine: if it has at least three different types that it works through to do this. Its class might be a Sprite, so it can draw itself in the game, but as far as the game model goes, it is a PlayerControlledActor, and as far as the controls go, it is a KeyboardControlledObject. Then, if need be, we can separate those roles into separate classes at any point. Perhaps the single ship will later become a cluster of separate ships. We can define a new class to manage a group of sprites, but if the new class is still a PlayerControlledActor and a KeyboardControlledObject, with a clearly defined, simple interface to each, then we should have little problem making this major change.
One somewhat bizarre possibility deserves mentioning: in a complex application, you might wish to go to the extent of having a single class with multiple roles treat itself as if it was three separate objects. The PlayerShip in the example above might have properties for Controller and Actor, each of which refers to itself. This will add a small amount of overhead in developing the class (it might have to say If Controller.Fire, rather than calling its own Fire function directly), but it would let the class be later separated into multiple classes more easily.
Whether to go to that extent depends on the likelihood of wanting to separate the class’s roles into separate classes later. It is not recommended that you should always code in this way by any means.
A detailed example: A Reverse Polish Notation Calculator
The Real Studio Curriculum Project provides an excellent example of an MVC application in the Simple RPN Calculator project.
Notice that the various classes and interfaces in the project have been separated into folders to clearly illustrate the MVC separation within the application.
In particular, notice how the StackDisplay class IsA ListBox, but that its role as the stack that the buttons and the TextField manipulate is via its Stack interface. Notice also that the TextField is also given a role in the model via the StackEntry interface. We don’t want to have to press Enter after typing in a number and before performing an operation, so this interface lets the stack object proper provide the StackEntry object with an opportunity to add its contents to the stack before a Pop operation.
Finally, notice that both the TextField and the buttons support the StackOperation interface, so that the stack on which they operate can be set in a single operation.
In summary, the various classes/interfaces, and their role in the MVC structure of the program are:
- The Stack interface is simply an object with the simple stack operations such as Push and Pop; and
- The StackEntry interface isn’t an intrinsic feature of a TextField, but rather allows the stack to correctly calculate its size (since the entry is part of the stack as soon as it contains a number), so we separate it into an interface here.
View Classes or Interfaces
- The StackDisplay class IsA ListBox. Note that we don’t explicitly separate its operations from those of the stack, since the operations in this case are the same: all changes to the abstract stack are reflected directly by equivalent changes to the display.
In a more complex application, this will usually not be the case.
Controller Classes or Interfaces
- The StackOperation interface lets us initialize all objects that will be working on the stack in the same operation;
- The StackOperationButton adds the StackOperation interface to the BevelButton;
- The NumEdit class is the standard numeric entry box we developed in an earlier project; and
- The StackEntryBox class adds the StackEntry and StackOperation interfaces to NumEdit.
Final Notes
It was not deemed necessary in such a simple application to fully separate the model from the view. From the outside, they are separated, but there is no explicit calling between the model and the view, since the operations on each are identical. You may find it useful to consider what would be involved in separating these roles. Perhaps make the stack a separate object from the ListBox that displays its content.
References
- Notes
- ↑ RPN stands for Reverse Polish Notation. It will be useful to look this up on Wikipedia or something similar, if you are not familiar with this way of representing calculations.
