Watching developers fall repeatedly into these traps, the Adobe Consulting team conceived the Model Locator pattern as a best practice for Flex developers. The Model Locator pattern is unique in Cairngorm because it is not borrowed from the Core J2EE Pattern catalog. Instead, we created this pattern specifically for Flex application development. Our motivation was to provide the ability to have Singleton model classes that hold state for the application and to enable this client-side state to reach the view classes.
It is important to ensure the model is a single instance, so data held in state can’t be duplicated or superseded by another instance. Multiple instances could lead to a number of data conflicts and should always be avoided. It is important to note, however, that an application is not limited to a single model; an application can have as many models as necessary, but only one instance of each.
Our Model Locator pattern strategy encourages the use of data binding so that view components bind directly to the client-side state held in the model. In this way, whenever the model is updated, all view components binding to the model receive notifications (through the underlying data binding mechanism) and update themselves to render the new model on the client.
In Cairngorm, we provide a marker interface for the Model Locator pattern, IModelLocator, to identify the models. Let’s focus on an example use of this pattern. In the Cairngorm Store we have a single class that implements the IModelLocator interface, com.adobe.cairngorm.samples.store.model.ShopModelLocator.
Take a few minutes to review the source for this ShopModelLocator class. In the Cairngorm Store, there is a notion of the "currently selected product":
public var selectedItem:ProductVO;
This is the item that the user has selected. The application stores an instance of a ProductVO in the Model Locator, uses the selectedItem property in the application to render the ProductDetails view, and uses the property to decide what to add to the shopping cart when the user presses the Add to Cart button.
In Part 3 of this series, you will learn how to use data binding to ensure that the currently selected item always appears in the product details view.
Additionally, through ShopModelLocator, you can find the list of products for sale in the Cairngorm Store by storing an ArrayCollection of these products on ShopModelLocator, as follows:
// ArrayCollection of ProductVOs public var products:ICollectionView;
Finally, there is a sophisticated ShoppingCart component that encapsulates all the functionality associated with adding and removing items to a list of items that the customer may want to purchase. The shoppingCart instance is stored on the ShopModelLocator and initialized within the ShopModelLocator’s constructor.
Having the Model Locator as a simple implementation of a singleton ensures that one and only one instance of a ShoppingCart exists per user.
The single instance of ShopModelLocator is initialized immediately when the main application is initialized. The Main.mxml file (the entry point for the Cairngorm Store) declares the following method as a handler for the creationComplete event on the main Application tag:
[Bindable] public var model:ShopModelLocator = ShopModelLocator.getInstance();
This code ensures that the entire client-side state (such as our shopping cart) correctly initializes when the application starts.
This brings up an important best practice. When you create components that rely upon client-side data, it is all too easy to create a direct reference to a Singleton model, such as ShopModelLocator. Indeed this is true throughout the application. We discourage this approach. Instead, consider passing the model and/or its properties down through a hierarchy of your view components, for a cleaner and more thoughtful solution.
As an example, the ProductDetails component in the Cairngorm Store requires only the currently selected item, so that it can display the name, description, price, and image of the item the user has selected. Because the currently selected item is stored in ShopModelLocator as an instance of the ProductVO called selectedItem, you can pass this item to the ProductDetails component as shown below:
<details:ProductDetails
id="productDetailsComp"
width="100%" height="325"
currencyFormatter="{ model.currencyFormatter }"
selectedItem="{ model.selectedItem }" />
The highlighted line of code shows that within the ProductDetails.mxml file, there is an attribute called selectedItem that is an instance of a Product VO. The code uses data binding (the curly brace notation) to ensure that anytime selectedItem on ShopModelLocator is updated, the ProductDetails view updates accordingly. we'll explore that mechanism more in Part 3.
In this article, we identified a number of challenges that consistently arise in the development of rich Internet applications, regardless of the business domain of the application. Throughout the series, we'll use these challenges to explore the Cairngorm development model in greater detail.
The first of these challenges is keeping state on the client; and it is important to note that the terms "state," "model," and "client-side data" are interchangeable in the development community. RIAs let you to break free of the request/response paradigm of HTML web applications. They enable you to keep data on the client as the user interacts with the application, but you must assume a certain degree of responsibility for that client-side state.
As you develop applications, you must address data integrity and consistency issues on the server—between the integration and business tier and, to a lesser extent, when sharing data between the business and presentation tier of an RIA. Remember that wherever an object model already exists on the client, you can take advantage of the Flex Server’s capabilities to map between the ActionScript and Java world, and keep a consistent object model on the client and server. Irrespective of this, representing client-side state as class objects with semantic meaning—rather than a mindless collection of strings, numbers, and Booleans—dramatically improves the readability and maintenance of your code base.
The Value Object (VO) pattern in Cairngorm, also referred to in the software engineering community as a Data Transfer Object (DTO) pattern, provides a sensible way of representing the client-side object model and helps developers make better decisions about where they keep that state on the client, how they share the client-side state between different components (that may have different ways of rendering the state), and how they ensure that the view is always showing the most up-to-date rendering of the state.
The Model Locator pattern, first introduced in Cairngorm, is a strategy for ensuring the state is held in a singleton class. This approach allows developers to pass client side state down through a chain of view components. The singleton nature of the Model Locator pattern ensures that you are not exposed to the risks of duplicate states on the client, gives a large development team a consistent place to share application-level state as instances of value objects, and enables developers to leverage the powerful data binding capabilities of Flex to ensure that the view is notified when the model has changed.
In Part 3, we'll focus on architecting the view, exploring in greater depth how to leverage Flex data binding with state stored on ShopModelLocator (the Model Locator implementation) while exploring more of the Cairngorm framework.
Developing Flex RIAs with Cairngorm microarchitecture – Part 3: Architecting the View