Mate

Mate is an event- and tag-based Flex framework. The API is in MXML tags. Mate-based applications are built using implicit invocation caused by dispatching and dependency injection of the results into views.

With implicit invocation, any interested object can listen to the events that are listed (with their handlers) in one or more MXML components of type <EventMap>. Any important action in the application should generate one of the events listed in this map. In Mate, as opposed to Cairngorm, an application developer can configure multiple handlers for each event and specify the sequence in which they should be invoked by assigning priorities in the event handler.

This section walks you through the Mate framework by analyzing its version of Café Townsend, created by the Mate team, which we encourage you to download from http://mate.asfusion.com/page/examples/cafe-townsend.

The data flow between Mate components while displaying a list of Café employees is depicted in Figure 1-6.

Bringing a list of employees with Mate

Figure 1-6. Bringing a list of employees with Mate

Mate is a much less intrusive framework than Cairngorm, as it does not force developers to add lots of boilerplate code in their applications. Figure 1-7 shows the project structure of the Café. The folder maps contains objects added to the Café project because it’s written using Mate (at least one event map is required). These objects are included in the main application as follows:

<maps:MainEventMap />
<maps:ModelMap />

All events that bubble up in Café will reach these map objects, which will process them according to the event handlers defined in these event maps.

Café Townsend Mate project structure

Figure 1-7. Café Townsend Mate project structure

Cairngorm relies on central repositories of events, services, and models; Mate promotes decoupling among business logic, events, and services. Mate does not force you to extend any classes. Just create an <EventMap> in your application object, define <EventHandler> tags there, and declare the services required for processing these events inside the handlers, i.e., <RemoteObjectInvoker>, <HTTPServiceInvoker>, or <WebServiceInvoker>. When your application grows, consider creating multiple EventMap objects to keep them manageable.

Example 1-10 depicts about half of the code of the MainEventMap.mxml from Café Townsend.

Example 1-10. Fragment of MainEventMap.mxml

<?xml version="1.0" encoding="utf-8"?>
<EventMap xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns="http://mate.asfusion.com/">

   <mx:Script>
      <![CDATA[
         import mx.events.*;
         import com.cafetownsend.events.*;
         import com.cafetownsend.business.*;
      ]]>
   </mx:Script>

   <!-- FlexEvent.PREINITIALIZE -->

   <EventHandlers type="{FlexEvent.PREINITIALIZE}">
      <ObjectBuilder generator="{AuthorizationManager}"
             constructorArguments="{scope.dispatcher}" />
   </EventHandlers>

   <!-- FlexEvent.APPLICATION_COMPLETE -->

   <EventHandlers type="{FlexEvent.APPLICATION_COMPLETE}">

      <HTTPServiceInvoker instance="{employeesService}">
         <resultHandlers>
            <MethodInvoker generator="{EmployeeParser}"
               method="loadEmployeesFromXML"
                   arguments="{resultObject}" />

            <MethodInvoker generator="{EmployeeManager}"
            method="saveEmpoyeeList" arguments="{lastReturn}" />
         </resultHandlers>
      </HTTPServiceInvoker>

   </EventHandlers>

   <!-- LoginEvent.LOGIN -->

   <EventHandlers type="{LoginEvent.LOGIN}">
      <MethodInvoker generator="{AuthorizationManager}" method="login"
              arguments="{[event.username, event.password]}" />

<!-- Because there is no server request, we just send the response right away.
   Normally, we would do this inside the resultSequence -->
      <ResponseAnnouncer type="loginResultResponse">
         <Properties loginResult="{lastReturn}"/>
      </ResponseAnnouncer>
   </EventHandlers>

   <!-- EmployeeEvent.SAVE -->

   <EventHandlers type="{EmployeeEvent.SAVE}">
      <MethodInvoker generator="{EmployeeManager}"
            method="saveEmployee" arguments="{event.employee}"/>
      <!-- assume everything was ok, make employee list show up -->
      <EventAnnouncer generator="{NavigationEvent}"
                 type="{NavigationEvent.EMPLOYEE_LIST}"/>
   </EventHandlers>

...
   <mx:HTTPService id="employeesService" url="assets/data/Employees.xml"
                           resultFormat="e4x" />
</EventMap>

In the example code, note the declaration of the handler of the system Flex event APPLICATION_COMPLETE with nested HttpServiceInvoker to get the data from Employees.xml via employeesService, which is defined at the very end of this map using the familiar <mx:HTTPService> tag. EventHandler objects match the type of the received event with the one specified in the type attribute in the map file.

When your application receives the result of the call to employeesService, it invokes the functions defined in the resultHandlers nested inside the service invoker. In our case, two methods listed in the result handler section are called sequentially: EmployeeParser.loadEmployeesForXML() and EmployeeManager.saveEmployeeList():

<resultHandlers>
      <MethodInvoker generator="{EmployeeParser}"
         method="loadEmployeesFromXML"
             arguments="{resultObject}" />

      <MethodInvoker generator="{EmployeeManager}"
            method="saveEmpoyeeList" arguments="{lastReturn}" />
  </resultHandlers>

The first method, loadEmployeeList(), gets the resultObject returned by the HTTPService. The second one, saveEmployeeList(), gets the value returned by the first method via a predefined Mate variable called lastReturn. This way you can chain several method calls if needed.

Example 1-11 shows that the method loadEmployees() converts XML into an ActionScript Array object and returns it to Mate, which, according to the event map, forwards it to the method saveEmployeeList() for further processing (see Example 1-12). The name saveEmployeeList() is a bit misleading, because this method does not persist data, but rather stores it in memory in an ArrayCollection object.

Example 1-11. EmployeeParser.as

package com.cafetownsend.business{
   import com.cafetownsend.vos.Employee;

   public class EmployeeParser {
    public function loadEmployeesFromXML(employees:XML):Array {
      var employeeList:Array = new Array();

      for each( var thisEmployee:XML in employees..employee ){
         var employee:Employee = new Employee();
         employee.email = thisEmployee.email;
         employee.emp_id = thisEmployee.emp_id;
         employee.firstname = thisEmployee.firstname;
         employee.lastname = thisEmployee.lastname;
         employee.startdate = new
         Date(Date.parse(thisEmployee.startdate));
         employeeList.push(employee);
      }
      return employeeList;
    }
  }
}

The EmployeeManager plays the role of the model here—it stores employees in the collection employeeList and information about the selected/new employee in the variable employee.

Example 1-12. The model: EmployeeManager.as

package com.cafetownsend.business{
   import com.cafetownsend.vos.Employee;
   import flash.events.Event;
   import flash.events.EventDispatcher;
   import mx.collections.ArrayCollection;

   public class EmployeeManager extends EventDispatcher {

   private var _employeeList:ArrayCollection;
   private var _employee:Employee;

   [Bindable (event="employeeListChanged")]
   public function get employeeList():ArrayCollection{
      return _employeeList;
   }

   [Bindable (event="employeeChanged")]
   public function get employee():Employee{
      return _employee;
   }

   public function saveEmpoyeeList(employees:Array):void {
      _employeeList = new ArrayCollection(employees);
      dispatchEvent(new Event('employeeListChanged'));
   }

   public function selectEmployee(employee:Employee):void {
      _employee = employee;
      dispatchEvent(new Event('employeeChanged'));
   }

   public function deleteEmployee (employee:Employee) : void {
     _employeeList.removeItemAt(_employeeList.getItemIndex(employee));
     selectEmployee(null);
   }

   public function saveEmployee (employee:Employee) : void {
     var dpIndex : int = -1;

    for ( var i : uint = 0; i < employeeList.length; i++ ) {
    // does the the incoming emp_id exist in the list
      if ( employeeList[i].emp_id == employee.emp_id ) {
      // set our ArrayCollection index to that employee position
         dpIndex = i;
      }
    }

   if ( dpIndex >= 0 ) {
      // update the existing employee
      (employeeList.getItemAt(dpIndex) as Employee).copyFrom(employee);
   } else {
      // add the employee to the ArrayCollection
      var tempEmployee:Employee = new Employee();
      tempEmployee.copyFrom(employee);
      employeeList.addItem(tempEmployee);
   }
   // clear out the selected employee
   selectEmployee(null);
  }
 }
}

So far, so good. The array of employees will be passed to the saveEmployeeList() function and placed for storage in the employeeList collection. But where’s the link between the Model and the View?

EmployeeList.mxml, located in the package view, has the fragment shown in Example 1-13.

Example 1-13. Fragment from the View: EmployeeList.mxml

 [Bindable]
public var employees:ArrayCollection = null;
...
<mx:List id="employees_li" dataProvider="{employees}"
labelFunction="properName" change="updateEmployee()" width="100%" />

And now let’s take a peek at the content of the second mapping object, called ModelMap.mxml, shown in Example 1-14. It uses Mate’s PropertyInjector object, which “injects” the value into the variable EmployeeList.employee from EmployeeManager.employeeList (there is one more PropertyInjector, which is irrelevant for our discussion).

Example 1-14. ModelMap.mxml

<?xml version="1.0" encoding="utf-8"?>
<EventMap xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="http://mate.asfusion.com/">
 <mx:Script>
   <![CDATA[
   import com.cafetownsend.business.*;
   import com.cafetownsend.views.*;
   ]]>
</mx:Script>

 <Injectors target="{EmployeeDetail}" >
   <PropertyInjector targetKey="selectedEmployee"
      source="{EmployeeManager}" sourceKey="employee" />
 </Injectors>

 <Injectors target="{EmployeeList}">
   <PropertyInjector targetKey="employees"
      source="{EmployeeManager}" sourceKey="employeeList" />
 </Injectors>
</EventMap>

If you sense a Dependency Injection design pattern, you’re right.

This pattern really helps you create loosely coupled components. Let’s revisit the code fragment of the view shown in Example 1-13. It’s written “assuming” that some outsider object will populate the variable employees. This code does not reach out for another specific component, demanding, “Give me the data!” It waits until someone injects the data.

And this someone is declared in ModelMap.mxml as follows:

<PropertyInjector targetKey="employees"
    source="{EmployeeManager}" sourceKey="employeeList" />

At this point, software developers familiar with Java Spring framework should feel at home. It’s the same concept. Objects never reach out for other object’s data—the plumbing is done in third-party declarative components (XML in Spring and MXML in Mate). The benefits are obvious: components don’t depend on one another. Just write the mapping file like ModelMap.mxml and specify the source and target for the data.

Another benefit is simplified testing—if the real data feed is not ready, create a mock model object and use it in the PropertyInjector tag. Switching to a real data model is just a matter of changing a couple of properties in this injector.

Creators of the Mate version of the Café Townsend application have decided to use EmployeeParser and EmployeeManager objects, but the Mate framework does not force you to separate parsing or any other business logic from the model. In this case, the parser could have injected the data directly to the View without even performing this loop converting XML into an array.

In the case of Cairngorm, a view that needs some data would reach out for the model by making a call like ModelLocator.getModelLocator().employeeList, which means that the view is tightly coupled with a ModelLocator object.

In the case of Mate injectors, the view waits to receive employeeList without making any remote procedure calls (RPCs).

Report Card: Mate

Mate is a nonintrusive MXML framework that offers flexible separation of the application views and processing logic. The application developers are not forced to do all of their plumbing exclusively via Mate and are free to use standard Flex event processing along with the EventMap object offered by Mate. Because it is tag-based, Flex developers will find it easy to program with. The learning curves of Mate and Cairngorm are comparable. Here’s the report card.

The pros are:

  • Mate is nonintrusive—Mate-specific code can be encapsulated in a handful of objects.

  • It’s MXML-based and allows you to keep using the Flex event model.

  • It promotes loose coupling between components by implementing dependency injection.

  • It’s well documented.

The cons are:

  • It hasn’t been officially released yet.

  • It doesn’t support working with Data Management Services offered by LCDS, and because of this you’d need to code this part manually.

As opposed to Cairngorm, using Mate in your application does not require developers to create many additional classes or components just to support the life cycle of the framework itself. This explains why the Mate version of the released Café Townsend SWF is about 10 percent smaller.

Mate promotes loose coupling between components by implementing a Dependency Injection design pattern. But loose coupling comes at a price—all communications in Mate are done via events, which have more overhead compared to direct function calls. Events require additional object instances to be created, as you don’t just call a function on some component, but have to create an instance of some event and dispatch it to that component. The receiving party has to create additional event listeners, which may become a source of memory leaking.

Function calls do not have these issues and offer additional benefit-type checking of arguments and returned values.

Mate also uses singletons, but they do not have to be instantiated by application developers. Application components are also instantiated by the framework as per MXML tags included in the EventMap object, which also performs the role of a class factory with lazy instantiation—if the event that required an instance of EmployeeManager was never triggered, the instance is not created. A special Boolean attribute cache on MethodInvoker and ObjectBuilder ensures that the instance will be garbage-collected.

Currently, Mate offers over 30 MXML tags, but this number can be increased by application developers. For example, by subclassing Mate’s AbstractServiceInvoker class, you can create a new tag that implements a service that’s specific to your application and can be invoked from EventMap, the same way other services can.

If your application uses Flex modules, Mate documentation suggests that you can place EventMap objects in the main application as well as in modules. But as with any framework that uses global objects (EventMap in this case), you can run into conflicts between events defined in the module’s map and the main application’s map. Of course, if modules are created to be used with only one application, you can come up with some naming conventions to ensure that every event has a unique name, but this may cause issues if you’d like to treat modules as functional black boxes that can be reused in multiple applications.

Mate does not offer UI controls; it does not include code generators to automate the development process. It does not support automatic data synchronization between the client and the server (LCDS Data Management Service) and would require manual programming in this area.

Mate is the youngest of all frameworks reviewed in this chapter. But even though (at the time of this writing) Mate hasn’t been released yet, it’s well documented.

Get Agile Enterprise Application Development with Flex now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.