MVC: A Compound Pattern - Advanced ActionScript 3: Design Patterns, Second Edition (2015)

Advanced ActionScript 3: Design Patterns, Second Edition (2015)

Chapter 10. MVC: A Compound Pattern

I have tried to take few shortcuts with the demonstrations of patterns to help reinforce previous patterns even as I teach you new ones. While this absolutely bloated the code, and perhaps increased the difficulty, I refused to pull any punches. The justification as to why I did not rely on simple and unrealistic demos was so you could see how they are truly used in the real world. Also, I believe that simplification can often dilute a necessary impact. I wanted to demonstrate that patterns are often utilized with other patterns and are not always individual solutions.

I covered 15 of the 23 original Gang of Four design patterns, and these 15 were not chosen at random. They are the most utilized design patterns in the ActionScript 3.0 language by professionals within the interactive industry. This does not mean you should choose to stop at these 15. Design patterns are time-tested solutions to recurring programmatic dilemmas. The more you know, and, more importantly, the more you understand, the better.

OOP teaches four principles: encapsulation, data-hiding, polymorphism, and inheritance. While these are great guidelines to follow, it’s often difficult to follow them to the letter, especially without having the proper tools to do so. OOP is a style of coding based on a thought process and nothing more.

Design patterns, on the other hand, are solutions that other object-oriented programmers have devised to fulfill aspects of object collaborations and object-oriented thought, thus further empowering OOP, which would otherwise cripple flexible code.

By making use of the tools you have just learned and by adding them to your programming repertoire, you can devise more separations among structures, adding to more reusable and more flexible code that may not have been possible before. One well-known pattern that illustrates this concept is the Model View Controller, or MVC.

OOP encourages the breakdown of complex problems into smaller, more manageable objects. This practice offers greater flexibility among objects utilized but also allows a problem to be solved incrementally. This can decrease an often overwhelming dilemma into smaller, more manageable hurdles to solve. Smaller issues are less cumbersome to get through as they often focus on fewer details.

In programming, the problem at hand is referred to as the problem domain (see Figure 10-1). The term domain refers to boundaries that encompass all details critical to the issue.

image

Figure 10-1. A localized problem that requires a solution

Each attempt to break this problem down into smaller, more manageable issues represents a subdivision of the problem domain—in addition to being a problem domain within itself. While it may be unnecessary, the subdivisions can continuously be broken down into even smaller problems (see Figure 10-2).

image

Figure 10-2. Deconstructing an overall problem domain into that of four smaller problem domains

As each subsection focuses on a particular aspect of a problem, each subdivision can be referred to as a problem domain, respectively (see Figure 10-3).

image

Figure 10-3. Any problem domain can continue to be made more granular

It’s often necessary to break down a larger issue into several smaller ones. This allows the mind to more easily comprehend the tasks necessary to solve such a problem. Of course, the more pieces of a puzzle, the more complicated it can become.

I have covered an extensive amount of solutions that you can now make use of to further the flexibility and communication between such granular objects.

As the Internet has expanded and computers have become much more powerful, the focus among applications has homed in on user experience and how the user interacts with the application. A means to incorporate the user and the application at one point in time was a problem to solve. Nowadays we can implement a design pattern that bridges the gap between the user and the application. This design pattern is known as the MVC.

The MVC: Model View Controller

Technical Overview

Intent: To provide users control over data as seen from multiple perspectives (see Figure 10-4).

image

Figure 10-4. Class Diagram

Parts

· Model

· View

· Controller

· Client

Benefits

· Increases cohesion of each aspect.

· Localizes a logic domain that can be easily maintained.

· Enables the Model’s independence from the UI.

Drawbacks

· The MVC is complex.

· Compounded drawbacks among the patterns that it contains.

· Changes among the interface of one component may affect another.

A Comprehensive Look

The Model View Controller, or MVC, is made up of three aspects, as the name suggests: the Model, the View, and the Controller. Trygve Reenskaug conceived its origins for the main purpose of allowing users to manipulate complex data sets via multiple presentations.

The first aspect of the MVC, being the Model, localizes the set of data, or states, and behaviors required in fulfilling the logic of the problem domain. This is referred to as the logic domain. The model, or logic domain, possesses the appropriate methods by which its data can be retrieved and mutated accordingly.

The participation of the Model can be that of either an active or passive role. As a passive participant, the model remains absent of any further duties, which pushes the onus of any change in state onto that of the Controller. As an active participant, the Model adopts the role of publisher/notifier, in which Views as well as Controllers can receive notifications of change in states via subscription, achieved with the Observer pattern (Chapter 8).

The View in the MVC represents a graphical representation of any number of particular aspects possessed by the Model. As one view may visually reflect only one chosen aspect of the model, there is no limit to the amount of views that can be used along with a Model.

As far as the role of the View, visual representation among the Model’s internal representation is its only focus. The absence of logic necessary to a problem domain within a View is what allows all visuals to be interchangeable among other Views.

The Controller in the MVC is the mediator between the user’s input and the logic domain. The role of the Controller is to provide the user the means to interact with the Model, as it possesses appropriate bindings among the Model’s interface. To achieve flexibility, a controller is assigned to a View often by way of the Strategy pattern (Chapter 7).

If the Model is not an active participant, the Controller must provide notification to the view it pertains to, so that it can properly remain synchronized with the Model. A View should only be notified after all states are properly assigned to the Model.

The MVC is a challenging pattern to grasp, as there are many variations regarding the particulars among the three objects that make up the triad. One reason for this is that the MVC is a compound pattern. In short, this means the MVC makes use of multiple design patterns already discussed to enable the three aspects to make use of one another. The most important understanding among the MVC is the separation among the three aspects, which provides distinct boundaries among the three components. This separation aims to ensure that relevant behaviors, or logic pertinent to each domain, remain intact when substituting or layering aspects of the triad.

The three components are:

1. The presentation, known as the View.

2. The system’s interface, logic, structure, or state,

3. (or all the above) known as the Model.

4. The interpreter among a user’s input to the system, known as the Controller.

Typically, the MVC makes use of the Observer, Strategy, Composite, and Façade design patterns, and, at its most granular state, is made up of 1:1:1 ratio.

The AS3 Cast

· Abstract Model – The Interface

o Because a Model is totally independent from Views and Controllers, they can be interchanged among various Views and Controllers. Therefore, a proper interface is required so that the data can be retrieved accordingly.

· Concrete Model – The Logic Domain

o It localizes and maintains the logic, state, and structure within a scope. The amount of data varies for the particular problem domain.

· Abstract View – The Views Abstract Class

o The Abstract View separates the associations between the Model utilized from the visual representations. Additionally, and optionally, the Abstract View can be the manufacturer of the expected Controller that will be used along with the View.

· Concrete View – Implements Specifics

o The Concrete View contains the necessary logic to instantiate the appropriate Controller.

· Abstract Controller – The Abstract Class of the Controller

o The Abstract Controller can be used to devise a common interface to which all Concrete Controllers can extend, allowing each to be substituted via the Strategy pattern. The Abstract Controller can also retain references to the View and Model, which it will make use of and supply any default behaviors that may be necessary.

· Concrete Controller – Implements Specifics

o The Concrete Controller implements the necessary means to bridge a user and the application. This means handling the aspects of the user interactions and interpreting those into requests of the Model.

· Client – The Messenger of the Model

o The client is most often the Controller, as it is the catalyst for any updates among itself and views. These requests may be triggered by a user’s actions directly or indirectly.

When It’s Useful

When form and function are bound too greatly to one another and deter reusability.

Demonstration

Trygve would be displeased if I did not mention that the most important aspect of the MVC is the user. The MVC’s purpose is to bridge the gap between the user’s mind and the computer.

The Model mirrors the user’s mental model, which is the user’s understandings/expectations imparted on the system. Consider your computer: regardless of its operating software, while it reads and writes bytes, it allows you (the user) to impart your vision into the confines of the computer and control how it presents stored bytes back to you. This would be the big-picture problem domain. As this example would be a rather huge demonstration, I will minimize the scale to that of a simpler problem domain.

For this demonstration, I will show you how to use the MVC to mirror the mental model of a user and how they interact with the icons on their desktop. (specifically the icon position and the icon name). You will be creating a model that holds relevant information of a folder/file within your application, such as the name and coordinates. It’s important to remember that the problem domain under the microscope is a particular magnification of the big-picture problem domain, and therefore must be constructed with that in mind.

I begin with the Model; for this demonstration it will be the simplest of the three aspects.

The Model

The Model for this demonstration must allow the user to bestow their interpretation of how they choose to interact with their data, in a way that appears as if the user is manipulating the actual data itself. In a sense, the Model must represent the decisions of the user as if it were an extension of their mind.

For the purpose of allowing a user to manipulate the position of an icon on their desktop, as well as the file name, the Model must properly possess the ability to retain the location as well as identification of an icon.

While your application will not be saving the user’s information, it’s not out of the realm of possibilities, just out of scope for this demonstration. What it will be doing instead is maintaining the data, which can be possibly broadcast to any object.

In order to provide a means of notification, you will make use of the Observer pattern. To separate the role of “subject” from the role of Model, you’ll begin with an abstract class known as AbstractModel, which will possess knowledge of possible observers that it will inform upon updates. You can achieve this behavior with the use of the ISubject interface; see Listings 10-1 and 10-2.

Listing 10-1. ISubject Exposes Two Methods, which Allow Observers to Register and Unregister from Notifications

public interface ISubject
{
function addObserver( observer : IObserve, aspect : Function ) : Boolean;
function removeObserver( observer : IObserve ) : Boolean;
}

Listing 10-2. AbstractModel Implements ISubject, which Allows the Addition and Removal of Observers

public class AbstractModel extends EventDispatcher implements ISubject
{
protected var dict : Dictionary;

public function AbstractModel( target : IEventDispatcher = null )
{
super( target );
dict = new Dictionary( false );
}

public function addObserver( observer : IObserve, aspect : Function ) : Boolean
{
dict[observer] = aspect;
return true;
}

public function removeObserver( observer : IObserve ) : Boolean
{
dict[observer] = null;
delete dict[observer];
return true;
}

protected function notify() : void
{
throw new IllegalOperationError( 'notify must be overridden' );
}
}

By extending AbstractModel, you can layer data specifics and dictate how the notification process occurs. Knowing that the Model retains the identity of each folder and its location, you can add the appropriate properties. While folders and documents will both posses similar attributes, you create a Model that is generic for both, as shown in Listing 10-3.

Listing 10-3. ConfigModel Subclasses AbstractModel and Supplies Additional Properties for UI Elements

public class ConfigModel extends AbstractModel
{
private var _name : String;
private var _xPos : int;
private var _yPos : int;

public function ConfigModel( target:IEventDispatcher = null )
{
super(target);
}

public function get name() : String
{
return _name;
}

public function set name( name : String ) : void
{
_name = name;
}

public function get xPos() : int
{
return _xPos;
}

public function set xPos( xPos : int ) : void
{
_xPos = xPos;
}

public function get yPos() : int
{
return _yPos;
}

public function set yPos( yPos : int ) : void
{
_yPos = yPos;
}
}

The View

Next, you will focus on the View. A View will retain a reference to both its Model and the Controller to which the View relies. When using any number of Controllers along with a particular View, the View can rely on the Strategy pattern as a way to supply and partition the logic. Very often, a View and Controller are built at the same time with each other in mind and therefore can be added to the View via the Factory method. This particular demonstration makes use of the latter of the choices.

There are many possible icons that a user can interact with on a computer. In an effort to generalize these icons and to standardize their uniformity, you’ll devise an appropriate abstraction.

AbstractView is the abstract class, which retains both the Model of the problem domain. As you know, your views will be subscribing to the model as observers, so it will implement the IObserve interface; see Listings 10-4 and 10-5.

Listing 10-4. IObserve Exposes an Interface, which Enables a Subject to Pass In Notifications

public interface IObserve
{
function notify(str:String):void;
}

Listing 10-5. AbstractView Extends IObserve in Order to Remain in Synch with the State of the Model

public class AbstractView extends Sprite implements IObserve
{
protected var _mdl : AbstractModel;

public function AbstractView( mdl : AbstractModel=null )
{
if (mdl)
this.mdl = mdl;
}

public function notify( str : String ) : void
{
}

public function get mdl() : AbstractModel
{
return _mdl;
}

public function set mdl( mdl : AbstractModel ) : void
{
_mdl = mdl;
_mdl.addObserver(this,null);
}
}

Having devised an abstraction that concerns itself mainly with the subscribing and unsubscribing, you now must add the abstract layer that focuses on the roles of a view. The role of the view must retain a reference to the model and its controller. AbstractFileSystemView will extend AbstractView and declare the Factory method that manufactures the View’s Controller. This will allow varied views to be more easily created and implemented; see Listing 10-6.

Listing 10-6. AbstractFileSystemView is the Superclass of each File View Component

public class AbstractFileSystemView extends AbstractView
{
protected var _strategy : AbstractController;

public function AbstractFileSystemView( mdl : ConfigModel )
{
super( mdl );
}

protected function createDefaultController() : AbstractController
{
throw new IllegalOperationError( 'createDefaultController must be overridden' );
return null;
}
}

Two possible views a user can expect are a folder and a file. As you might think, a folder makes use of composition whereas a file remains a leaf component. With this in mind, your MVC could make perfect use of the Composite pattern. In order to accomplish this, your abstraction must be properly layered with aspects of a component from the Composite pattern. The component possesses the appropriate references (such as image icon, textfield, and the parenting composite) that both file and folder require. This allows them to be indistinguishable from one another. You’ll devise yet another extension of AbstractFileSystemView, which will become your component (see Listing 10-7).

Listing 10-7. OSComponent Extends AbstractFileSystemView and Layers Added References

public class OSComponent extends AbstractFileSystemView
{
protected var _fileName : String;
protected var _field : DisplayField;
protected var _representation : Bitmap;
protected var _parentComposite : OSComponent;
static protected const PADDING : int = 4;

public function OSComponent( mdl : ConfigModel )
{
super( mdl );
}

protected function dispatch( event : MouseEvent ) : void
{
event.stopPropagation();
dispatchEvent( event );
}

public function open() : void
{
}

public function close(): void
{
}

public function get field(): DisplayField
{
return _field;
}
}

Lastly, you add one additional layer to the OSComponent class that will contain the specifics of a Leaf within your application (see Listing 10-8).

Listing 10-8. LeafView Extends OSComponent and Supplies the Concretes Among Each Factory Method

public class LeafView extends OSComponent
{
[Embed(source="/Users/FeZEC/workspace/CreationalPatterns/ bin/Tree_defaultLeafIcon.png")]
private var embeddedClass: Class;

public function LeafView( mdl: ConfigModel = null )
{
super( mdl );
_representation = createIcon();
addChild( _representation );
_field = createDisplayField();
_field.y = this.height + PADDING;
_field.x = (this.width - _field.textWidth) * .5;
_field.addEventListener( MouseEvent.MOUSE_DOWN, dispatch );
_field.addEventListener( MouseEvent.MOUSE_UP, dispatch );
addChild( _field );
_strategy = createDefaultController();
}

protected function createDisplayField(): DisplayField
{
return new DisplayField();
}

protected function createIcon(): Bitmap
{
return new embeddedClass() as Bitmap;
}

override protected function createDefaultController(): AbstractController
{
return new ComponentRenamerController ( _mdl, this );
}
}

DisplayField is a textfield that possesses two additional operations: rename and display. These two methods toggle between the dynamic and input type of the textfield to allow for file/folder renaming (see Listing 10-9).

Listing 10-9. DisplayField is Responsible for Displaying a File/Folder Name

public class DisplayField extends TextField
{
public function DisplayField()
{
super();
autoSize = TextFieldAutoSize.CENTER;
height = 10;
width = 1;
embedFonts = false;
display();
}

public function rename(): void
{
var end : int = this.text.indexOf( '.' );
type = TextFieldType.INPUT;
selectable = true;
setSelection( -1, (end > -1) ? end: text.length );
border = true;
}

public function display(): void
{
type = TextFieldType.DYNAMIC;
selectable = false;
border = false;
}
}

The Controller

The final aspect of the triad is the Controller. As a Controller may require interchangeability among a system, it requires a superclass from which all similar Controllers extend. Because the Controller receives the input of a user, the Controller is expected to make changes within the Model for the view, coordinate changes of a View, or both. This depends on how you choose to use each aspect to communicate. Either way, a Controller must retain references of View and Model; therefore you can supply a superclass known as AbstractController, which will retain these references (see Listing 10-10).

Listing 10-10. AbstractController is the Superclass of all Controllers

public class AbstractController extends Object
{
protected var _mdl: AbstractModel;
protected var _view: AbstractFileSystemView;

public function AbstractController( mdl: AbstractModel=null,image
view: AbstractFileSystemView = null )
{
_mdl = mdl;
_view = view;
}

public function get mdl(): AbstractModel
{
return _mdl;
}

public function set mdl( mdl: AbstractModel ) : void
{
_mdl = mdl;
}

public function get view(): AbstractFileSystemView
{
return _view;
}

public function set view( view: AbstractFileSystemView ) : void
{
_view = view;
}
}

What makes the Controller necessary is how each Controller handles the user’s input relative to a View/Model. To demonstrate how a Controller coordinates changes with the View as well as the Model, you will devise a ComponentRenamerController, which makes use of theDisplayField within each View. Much like that of PC/Mac, a folder/file can be renamed if a user double clicks on the field within a particular range of milliseconds. ComponentRenamerController reflects that behavior on each View (see Listing 10-11).

Listing 10-11. ComponentRenamerController Allows for the Renaming of Each View in Your Problem Domain

public class ComponentRenamerController extends AbstractController
{
protected var _display: DisplayField;
protected var _timer: int;
protected const MAX_DURATION: int = 1000;

public function ComponentRenamerController( mdl: AbstractModel, view: OSComponent )
{
super( mdl, view );
_timer = 0;
_display = view.field;
_display.addEventListener( MouseEvent.CLICK, onMouseClick );
_display.addEventListener( FocusEvent.FOCUS_OUT, onOut );
_display.addEventListener( KeyboardEvent.KEY_DOWN, onPossibleEnter );
}

private function onPossibleEnter( event: KeyboardEvent ): void
{
switch(event.keyCode)
{
case Keyboard.ENTER:
commit();
break;
}
}

protected function onOut( event: FocusEvent ): void
{
commit();
}

protected function commit(): void
{
_display.display();
ConfigModel( _mdl ).name = _display.text;

Mouse.cursor = MouseCursor.ARROW;
}

private function onMouseClick( event: MouseEvent ): void
{
var currentTimer: int = getTimer();
if ( ( currentTimer - _timer ) < MAX_DURATION )
{
_display.rename();
}
_timer = currentTimer;
}
}

With your currently implemented code, you can make use of the DocumentClass to test the functionality of your use case scenario. You’ll use the DocumentClass to instantiate your Model, and to it you will supply defaults for each piece of data, which could represent the default settings. The Model of another problem domain will supply this data, but for now you can simply add the Data here to witness its effects (see Listing 10-12). Next, a View is instantiated and the Model is supplied as a parameter.

Listing 10-12. DocumentClass Supplies Data to a Model, then Associates the Model with a View

public class DocumentClass extends Sprite
{
public function DocumentClass()
{
stage.scaleMode = StageScaleMode.EXACT_FIT;
stage.align = StageAlign.TOP_LEFT;
mouseEnabled = false;

var cm : ConfigModel = new ConfigModel();
cm.name = "Default_Text.txt";
cm.xPos = Math.random() * this.stage.stageWidth;
cm.yPos = Math.random() * this.stage.stageHeight;
addChild( new LeafView( cm ) );
}
}

Once the application is running, by clicking down on the mouse twice slowly within one second on the display field, the display field of the presentation will turn into an input field, allowing the user to change the given name of the component. As the user is able to manipulate the text within the field, the Model only requires updating the currently stored filename to that chosen by the user.

Since the Model is unaware of either View or Controller, the task of updating the Model falls on the Controller.

This demonstration should arouse specifics points regarding the Model/View/Controller Pattern.

The first noteworthy point is that this particular MVC demonstration has made no use of the Strategy pattern or, arguably, the Observer pattern. I say “arguably” because you did add the ability into the code. It did, on the other hand, make use of the Template method and the Factorymethod. As a Compound pattern, the MVC is most concerned with the boundaries of presentation and structure. What this should illustrate is that the utilized patterns are not what make the MVC, but rather the Model, View, and Controller objects itself. Don’t feel limited to the patterns that must be utilized along with them.

The second point worthy of noting is the tight boundaries among the three aspects. Not one allowed its code to bleed into a domain to which it did not belong, which allows for easier interchanging among aspects.

During the implementation of a given aspect, you may be compelled to write View-specific logic within the Model, or vice versa. Keeping the boundaries tight allows you the ability to achieve multiple views between a Model, vary Controllers among a system, etc.

FAQ

· Q: If this was a 1:1:1 MVC; is it safe to say that any aspect may overwhelm other aspects?

· A: Absolutely. The 1:1:1 MVC is the simplest form of the MVC. Just because the name of the pattern suggests 1 Model, 1 View, and 1 Controller, remember that the name is focusing on the boundaries of these aspects and nothing more. Your application may make use of several Views, 1 Model and 12 Controllers. Another possibility is that 1 Controller may need to coordinate 13 views. You get the picture.