About MVC (Model-View-Controller) Designs

LANSA

About MVC (Model-View-Controller) Designs
Example 202 - Rating: advanced
The Things that Make Up this ExampleTo Execute this Example

The MVC (Model-View-Controller) approach to designing user interfaces became popular as part of the Smalltalk language. This approach (and many variations of it) is still used in applications and it is an effective way of designing complex and highly responsive user interfaces in some situations.

This example demonstrates some of the basic features of an MVC style design implemented in Visual LANSA application. It consists of a series of forms that looks like this:



These forms work with an object know as a "Product". A product has an identifying number, a description, a price and an available quantity. The forms shown are:
  • S_202FBR (top): which browses the list of all available products.
  • S_202FCR (left): which allows new products to be created.
  • S_202FDL(right): which allows existing products to be deleted.
  • S_202FUP(bottom): which allows existing products to be updated.
In MVC terminology, these forms are all "views" of the object that is managed by the "model". Behind these forms are two additional non-visual components:
  • S_202RCO: which acts as the "controller" for this example.
  • S_202RMO: which acts as the "model" for this example.
Conceptually, all these Visual LANSA components could be visualized like this:



The key feature of this design is that each "view":
  • Performs all changes to a product via a common "controller" named S_220RCO
  • Listens to (or observes) the single shared instance of the "model" S_202RMO and responds to messages that it broadcasts (these interactions are shown in red).
This approach means that each individual form can be made highly aware of the consequences of actions that the other forms take and respond as required.

You can demonstrate this inter-form awareness by working through the following increasingly complex scenarios (to understand in detail how and why this all happens you will have to look at the RDMLX source code for the S_202* examples):

Start form S_202FBR then:
  • Use S_202FCR to create a new product. Note how it appears in the browser product list the instant that it is created. This happens because the browser (S_202FBR) is listening to the model (S_202RMO) for ProductCreated events.
  • Use S_202FDL to display and then delete a product. The product instantly disappears from the browser product list. This happens because the browser (S_202FBR) is listening to the model (S_202RMO) for ProductDestroyed events.
  • Use the deleter S_202FDL to display a product (but don’t press the delete button). Then use the updater S_202RUP to display the same product. Change the products description and update it. Note how the product details change in both the browser (S_202FBR) and in the deleter (S_202FDL). This happens because both the browser (S_202BR) and the deleter (S_202FDL) are listening to the model (S_202RMO) for ProductUpdated events.
  • In the updater (S_202FUP) and the deleter (S_202FDL) key in a non-existent product number (e.g.: 4444). No product details are displayed in either form. Now move to the creator (S_202FCR) and create a product with the number (e.g.: 4444). Instantly the new product details appear in the browser (S_202FBR), the updater (S_202FUP) and the deleter (S_202FDL) forms. This happens because all of these forms are listening to the model (S_202RMO) for ProductCreated events.
Notes and Suggestions

The essence of this example is that S_202FBR, S_202FCR, S_202DL and S_202UP all require access to a single shared instance of S_202RMO (the model) so that they can all listen to the same set of broadcast signals/messages.

If each of them simply declared S_202RMO (the model) in a normal DEFINE_COM, even if they used Scope(*Shared), there actually would be 4 instances of S_202RMO in existence and when an S_202RMO issued a "broadcast" signal only the form that actually defined it would "hear" the message.

To solve this problem we declare S_202RMO (the model) in S_202RCO (the controller) as Scope(*shared). Next a property is defined that can return a reference to the single shared instance of S_202RMO (the model) to the invokers (i.e.: one of S_202FBR, S_202FCR, S_202DL and S_202UP).

In each of S_202FBR, S_202FCR, S_202DL and S_202UP we then declare a dynamic reference to S_202RMO (the model) and set it to the property value returned by S_202RCO (the controller).

In other words, when S_202FBR, S_202FCR, S_202DL and S_202UP start up they ask their own instance of S_202RCO (the controller) to give then a reference to the single shared instance of S_202RMO (the model) …. so that they can listen to it.

This means that S_202FBR, S_202FCR, S_202DL and S_202UP can now listen to (via EVTROUTINE commands) or even instruct (via INVOKE commands) the single active instance of the model (S_202RMO).

Note: Scope(*Shared) makes sense only when all instances of the component defining the shared thing are of the same type. In this case, when the 4 instances of S_202RCO define S_202RMO as scope(*shared) they do actually share a single instance. If S_202FBR, S_202FCR, S_202DL and S_202UP all declared S_202RMO as scope(*shared) there would still be four separate instances of S_202RMO active because S_202FBR, S_202FCR, S_202DL and S_202UP are all different types of things.

Keywords
Example 202Model-View-Controller
MVCModel
ViewController