The MVC Framework

Notice that only in a few places does our code for the Stoplight Simulator call the application-specific methods of Stoplight. This suggests that it's possible to rewrite much of this code in an application-independent and therefore reusable way. We will call this our MVC framework. In the future, applications like Stoplight Simulator can be constructed as customizations of the MVC framework. Presumably this code could be written by domain experts with little or no knowledge of the underlying MVC design or technology. One of the design goals of a good framework is to make the job of customization as easy as possible.

 

Of course our MVC framework could support many types of applications:

Notes

·        Applications built on top of the MVC framework are called customizations.

·        Developers who build customizations are called customizers.

MVC Use Cases

MVC-based applications have three menus: File (red use cases), Help (green use cases), and Edit (yellow abstract use case):

A diagram of a user

AI-generated content may be incorrect.

Notes

·        The Save As use case prompts the user for a file name.

·        Save is a special case of Save As because it only prompts the user for a file name the first time it is executed.

·        If there are unsaved changes to the application data, Quit, Open, and New prompt the user to save them.

·        The Edit use case is abstract because it depends on the application. For example, a word processor might provide three ways to edit a document:

The Model

Let's begin by generalizing the Stoplight. In the MVC Architecture the Stoplight plays the role of the model, so let's introduce an abstract Model class into the framework.

A diagram of a model

Description automatically generated

A few innovations have been added. The Model keeps track of the file name where it can be saved. Initially this is null, indicating that the model has not yet been saved to a file. Model also has a flag unsavedChanges, initially false. When set to true this flag indicates that the model contains changes that have not yet been saved to a file. Each time the model is saved to a file this flag is set to false. The changed method sets the flag to true and notifies any subscribers.

The View

A diagram of a flowchart

Description automatically generated

The view maintains a pointer to the model. It also registers itself as a subscriber to the model.

When a new model is created, either by choosing New or Open from the File menu, the view needs to remove itself as a subscriber from the old model and add itself as a property subscriber to the new model. Be careful. The lists where listeners are stored inside of the Bean class are not written to the file, so they need to be reallocated by calling the Bean's initSupport method.

The App Panel

The App panel is the hub of the MVC framework. It contains the gray view panel, the pink control panel (containing the controls and other buttons). It’s frame contains the title bar and the menu bar with its File, Edit, and Help menus.

Graphical user interface, application

Description automatically generated

Here’s the design so far:

A diagram of a software company

Description automatically generated

 

The app panel implements the ActionEventListener interface. It listens for action events coming from the control panel or the menus.

The AppPanel also listens for action events fired by the model. This is useful if, for example, a menu item needs to be disabled when the model is in a state that prohibits the command generated by that menu item.

The App Factory

Handling the File menu can be done without knowing anything about the customization. But what about the Edit and Help menus?

The Help menu contains Help and About items. When selected they display dialog boxes containing application-specific information. How can the framework know what to display?

The situation with the Edit menu is even more complicated because the items under this menu are application-specific. How will the framework know what goes under this menu and how will it know what to do if these items are selected?

In general, there is a lot of application specific information the framework needs to know in order to build the application. This situation is not uncommon. We know how to build something, but we don't know how to build its components. In this case we know where everything goes. We know where the view and model go. We know where the Edit menu items go. We know where the title goes and we know where to display help and about information. We just don't know what these components are!

We can solve this seemingly unsolvable problem by using the Abstract Factory Pattern.

For MVC we define an abstract factory interface:

By implementing this interface customizers can tell us how to build the needed components.

Commands

The situation with the Edit menu is a bit more complicated. Using getEditCommands we can find out the names of the items that go under the Edit menu. But how will the framework know what to do when one of these items is selected by the user?

To solve this problem we use the Commands-as-Objects pattern (aka the Command Processor pattern). The idea is pretty cool. We are trained to see everything in the application domain as objects—stoplights, bank accounts, people, etc. Why not see everything in the solution domain as objects—programs, compilers, commands, etc?

Each time an Edit menu item is selected, we generate a command that can be shipped off to a command processor for execution. We can use the makeEditCommand method in the factory to tell us which type of command to create for each menu item.

Here's the implementation of ChangeCommand:

public class ChangeCommand extends Command {

   public ChangeCommand(Model model) {
     super(model);
   }

   public void execute() throws Exception { 
     if (!(model instanceof Stoplight)) {
        throw new Exception("Model must instantiate Stoplight");
     }
     Stoplight light = (Stoplight)model;
      light.change();
   }
}

Note

There is no need for ChangeCommand to maintain a pointer to Stoplight. The pointer is inherited from the abstract Command class (and is initialized by the Change class constructor). It only needs to be (safely) cast to Stoplight in the execute method.

Error Handling

The abstract execute method in the Command class may throw an exception. All exceptions should be caught by the actionPerformed method in the AppPanel class:

public void actionPerformed(ActionEvent ae) {
   try {
     // handle control actions here
   } catch (Exception e) {
   handleException(e);
   }
}

protected void handleException(Exception e) {
Utilities.error(e);
}

The default policy of the framework is to simply display the message in an error dialog, but the handleException method can be overridden in a subclass in case a customizer has other ideas about what to do with exceptions.

The Design

Here's the complete design of the mvc framework:

A diagram of a computer

Description automatically generated

The following zip file contains complete design as well as fragments that can be imported into future StarUML models

·        fragments.zip

Implementation

Create a project called mvcApps. Within that project create a package called mvc. mvc customizations will be implemented in separate mvcApp packages.

The mvc package should contain the files:

·        Utilities.java

·        SafeFrame.java

·        Publisher.java

·        Subscriber.java

·        View.java

·        AppPanel.java

·        AppFactory.java

·        Command.java

·        Model.java

Notes

The ControlPanel class is nested inside the AppPanel class.

Consider modifying one of your earlier implementations of AppPanel.

Sample customizations

Stoplight Simulator 2.0

Brick CAD