Archive for the 'Object Oriented Design' Category

IResponder and Cairngorm

Friday, November 23rd, 2007

My original post on Cairngorm and IResponder had accidentally been deleted while updating my moderations queue, and many of you have contacted me stating that the post is no longer available so I will re-iterate what I mentioned in that post.

For some time now I have been entertaining the notion of abstracting IResponder implementations from Command classes into separate, discreet classes which are intended to handle asynchronous service invocation responses. There has been some talk in the Cairngorm community recently regarding Cairngorm Commands and IResponder implementations so I thought I would share my thoughts on the subject.

Typically most Cairngorm Events are handled by an associated Command. The Command handles the Event by updating a model on the ModelLocator, and / or instantiating a Business Delegate to manage invoking a middle-tier service.

At this point one could argue that the Command has finished doing it’s job - handling the Event. Let’s clarify by taking a look at a formal definition of the Command Pattern:

“The Command pattern is a design pattern in which objects are used to represent actions. A command object encapsulates an action and its parameters.”

From this we can deduce (in the context of a Cairngorm Event) that the Event is the “action” and the Command is the object which encapsulates the handling of the Event (action). The actual handling of the Service response could be considered a separate concern which is outside of the direct concern of the Event and Command, thus requiring an additional object to handle the service response.

However for many developers it is (by design) typical to simply have the Command implement IResponder and also handle the response from the service in addition to the actual Event. This makes sense from a convenience perspective, but not necessarily from a design perspective.

What I have been experimenting with is pretty simple and straightforward. It involves having a completely separate object implement IResponder and handle the service response directly.

Consider the following example in which a specific use-case requires an account log in. The following classes would be required: LoginEvent, LoginCommand, LoginResponder and LoginDelegate. Utilizing a separate class as the responder is very simple and straightforward and would be implemented as follows:

package events
{
  import com.adobe.cairngorm.events.CairngormEvent;
  import vo.LoginVO;

  public class LoginEvent extends CairngormEvent
  {    
    public static const LOGIN_EVENT:String="LoginEvent";
    public var vo:LoginVO;

    public function LoginEvent(vo:LoginVO)
    {
       super ( LOGIN_EVENT );
       this.vo = vo;
    }
  }
}

So far nothing different here, the above Event is just like any other CairngormEvent. Now let’s take a look at the Command implementation.

package commands
{
  import com.adobe.cairngorm.commands.ICommand;
  import com.adobe.cairngorm.events.CairngormEvent;
  import events.LoginEvent;

  public class LoginCommand implements ICommand
  {
    public function execute(event:CairngormEvent)
    {
      var evt:LoginEvent = event as LoginEvent;
      var re:IResponder = new LoginResponder();

      var delegate:LoginDelegate = new LoginDelegate(re);
      delegate.login(evt.vo.username, evt.vo.password);
    }
  }
}

Based on the above example, the only method which must be implemented is execute(), as defined by ICommand. The body of the execute() implementation instantiates an instance of LoginResponder and LoginDelegate, the LoginResponder instance is passed to the LoginDelegate as the IResponder instance.

As can be seen in the following example, the Business Delegate implementation is the same as any other typical Cairngorm Delegate:

package business
{
  import com.adobe.cairngorm.business.ServiceLocator;
  import mx.rpc.AsyncToken;
  import mx.rpc.IResponder;
  import mx.rpc.http.HTTPService;
  import vo.LoginVO;

  public class LoginDelegate
  {
    private var responder:IResponder;
    private var service:HTTPService;

    public function LoginDelegate(responder:IResponder)
    {
      this.responder = responder;
     
      var sl:ServiceLocator = ServiceLocator.getInstance();
      service = sl.getHTTPService( Services.LOGIN_SERVICE );
    }
   
    public function Login(vo:LoginVO) : void
    {
       var call:AsyncToken=service.send(vo.username,vo.password);
       call.addResponder( responder );
    }
  }
}

The IResponder implementation would be as follows:

package responders
{
  import mx.rpc.IResponder;

  public class LoginResponder implements IResponder
  {
    public function result(data:Object)
    {
      // result implementation…
    }

    public function fault(info:Object)
    {
      // fault implementation…
    }
  }
}

That’s pretty much it. Clean, simple, and yes, more code, however this design supports clean separation of concerns and promotes encapsulation and code reusability.

At the end of the day it really comes down to personal preference. For me, I always prefer to have more classes which encapsulate very specific tasks and responsibilities. As long as you have a clear and concise, but most of all, consistent design you usually can’t go wrong.

AS3 StringTokenizer

Thursday, November 1st, 2007

One useful utility in the java.util package is the StringTokenizer class. I was looking for an ActionScript implementation the other day but was unable to locate one. So after determining the level of effort to write a StringTokenizer in ActionScript was minimal, I decided to roll my own.

The ActionScript StringTokenizer is a convenience class which provides a simple mechanism from which Strings can be extracted into individual tokens based on a specific delimiter.

StringTokenizer provides an IIterator implementation as well as an IEnumeration implementation, thus allowing a StringTokenizer to be implemented as an IIterator or IEnumeration. The interfaces are also provided with the StringTokenizer API.

Ideally a StringTokenizer is to be utilized to tokenize a String into specific tokens and is to be considered read-only. To use StringTokenizer is very simple. The constructor accepts two arguments, both of which are required. The first argument is the source String from which tokens are to be extracted. The second argument is the delimiter from which the tokens are to be based on, as in the following:

var st:StringTokenizer = new StringTokenizer("R.I.A",".");

In addition to the constructor I have provided two static factory methods for creating both the IIterator implementation and IEnumeration implementation. The factory methods require the same arguments as the constructor and are provided as an alternative to the constructor.

var it:IIterator;
it = StringTokenizer.createIterator("R.I.A",".");

// or

var enum:IEnumeration;
enum = StringTokenizer.createEnumeration("R.I.A",".");

All implementations provide operations for determining if elements (tokens) remain as well as operations for retrieving the next element.

A typical StringTokenizer implementations is as follows:

var st:StringTokenizer = new StringTokenizer("R.I.A",".");

while ( st.hasMoreTokens() )
{
   trace (st.nextToken() );
}

// outputs
// R
// I
// A

An IIterator implementation would be as follows:

var it:IIterator;
it = StringTokenizer.createIterator("R.I.A",".");

while ( it.hasNext() )
{
   trace (it.next() );
}

// outputs
// R
// I
// A

An IEnumeration implementation would be as follows

var enum:IEnumeration;
enum = StringTokenizer.createEnumeration("R.I.A",".");

while ( enum.hasMoreElements() )
{
   trace (enum.nextElement() );
}

// outputs
// R
// I
// A

In each use-case the StringTokenizer instance will return the same result as the underlying StringTokenizer implementation (e.g. invoking IIterator.next() results in a call to StringTokenizer.nextToken(), etc).

Source:
StringTokenizer
IEnumeration
IIterator

Configurable ContextMenu API update / example

Sunday, October 28th, 2007

I have received numerous requests for an example which demonstrates how to implement the ConfigurableContextMenu API which I had developed back in February of this year.

In the time since the original post I have re-factored many of the core features, however the goal of the ConfigurableContextMenu API remains the same.

The following is a brief recap from the original post.

The ContextMenu classes in ActionScript 3: ContextMenu, ContextMenuItem, ContextMenuBuiltInItems provide a good base for working with context menus in Flex 2, however they do not provide an intuitive API for developing and working with custom context menus, especially at runtime, so in this regard these classes fall short to some degree.

In order to provide a solution which addresses the issues mentioned above I have developed a ConfigurableContextMenu API which allows developers to dynamically create custom context menus by which items can be added, removed, enabled, disabled, shown, hidden and cloned dynamically at runtime. This API also addresses certain Flash Player restrictions which fail silently in the ContextMenuItem class such as caption collisions and reserved captions restrictions which are simply ignored by Flash Player, leaving the developer clueless (until reading the documentation) as to why the items have not been created.

Below I have provided links to ConfigurableContextMenu resources which include complete documentation, UML class diagrams and basic example Flex application.

example
asdoc
uml diagram

The ConfigurableContextMenu API is published under the MIT license.

Enforcing an all static API in ActionScript 3

Friday, September 28th, 2007

It is quite common when designing an API or system in Adobe Flex that you will identify certain areas which call for specific classes to contain an all static API.

Typically, all-static classes are utilized as helper, utility and factory classes which provide static methods for performing common utility methods.

A commonly overlooked aspect of such designs is the assumption that an all-static class will not be misused by clients, specifically via instance instantiation, as it is assumed by the designer that an all static class would never be instantiated as there are no instance members available, only static class members. This is a fair assumption. However it is not possible to guarantee this will never occur as ActionScript 3 does not support private constructors (yes, I am back on this subject yet again). Although this is an unfortunate limitation of the language it should not deter you from enforcing such restrictions.

To help facilitate these restrictions in my own designs I have created a very simple, yet effective Abstract class called AbstractStaticBase, which helper and utility classes can extend in order to ensure they are never instantiated.

Classes which contain an all static API can extend AbstractStaticBase in order to ensure they can never be instantiated. This is the only requirement.

AbstractStaticBase is lightweight as it only contains a constructor. The constructor does nothing more than create an Error object and parse the call stack to determine the fully qualified name of the concrete class which has been instantiated, the message property is then set on the Error object and thrown.

Implementation on the clients part is very straightforward as all that is required is to extend AbstractStaticBase.

Consider the following example. CalcUtil is an all static class, to ensure an instance of CalcUtil is never instantiated simply extend AbstractStaticBase as follows:

package com.domain.utils
{
    import com.ericfeminella.AbstractStaticBase;

    public class CalcUtil extends AbstractStaticBase
    {
         public static function calculate() : Number
             {
             // implementation not pertinent to subject
         }
    }
}

// an attempt to instantiate CalcUtil …
var util:CalcUtil = new CalcUtil ();
 
// results in the following exception:
// Illegal instantiation attempted
// on class of static type: com.domain.utils::CalcUtil
 

So if you want to enforce that static classes are never instantiated, simply extend AbstractStaticBase.