I don’t presume to place myself at the status of GoF or Martin Fowler, but I came up with a concept that was then improved upon by my twin brother, Tyler, which I feel could stand to be called a pattern. I may not be the first one to do this, but if I am, then it really shouldn’t be called a pattern since design patterns are observable patterns found in a lot of code that can be documented and generalized. I apologize in advance if it should not hold “pattern” status, but I think it is worth discussing, so please be kind. It’s pretty simple in concept, and has turned out to be quite useful in practice. I call it the Response pattern.
Services (being used in the sense of web services being consumed) or other asynchronous operations must be responded to and handled correctly. Often the data returned will be formatted before it is passed on to the caller for consumption. Sometimes the data has been retrieved previously and cached by the system, requiring a check to the cache before calling the service.
Two existing solutions exist, the Callback and the Subscribe solution. Both of these have their drawbacks.
The Callback is a simple solution in which a method is passed in as a parameter of the service call. This method will then be called and passed in the results of the service call once the asynchronous operation is complete. While this is easy and simple, it only allows one method to respond to the call and additional complexity must be added to handle errors which might occur.
There needs to be a way to handle the results of a service call that:
- allows you to deal with the results of a specific call (not any that match some event name)
- handles errors
- allows more than one method to respond to an operation
- allows methods to format the data in set order
- allows synchronous operations with the same method (e.g. a cached item in memory can be returned)
- Service: An object or objects which facilitate calling remote system asynchronously. This may be a webservice API but may also include loading resources such as images.
- Handlers: A “handler” is a method or function which handles the results or errors of a call from the service.
- Formatters: A handler which formats the data or converts it into a different data type for other methods handling the results or errors.
- Response: An object that stores the handlers and formatters and calls them when the service results (or errors) are returned.
When a call is made on a service, the service returns a Response object. The caller of the service may then add handlers to the response object. When the call is completed or receives an error, the response object is told to call the result handlers or the error handlers. Each result handler will have at least one parameter which the data from the result (if any) will be passed into. Each error handler will have at least one parameter which the error will be passed into. If a handler method returns a value, that value will become the new data/error. Thus a handler becomes a formatter taking the data it receives and converting it or changing it to a new type of value.
This solution allows for layers of services which abstract the lower level from the higher. The following is an example of how the response might be used.
A system depends on a RESTful XML web service. A low-level service class has been created which loads HTTP data, we’ll call it WebLoader. This class uses the response pattern and is used to load images, documents, etc. from the web. Another service class called XMLLoader is layered on top of WebLoader, that is, it uses a WebLoader object to handle the low-level loading of an XML document from the web, but then converts that document into an XML object the system can use. Finally, a system specific service is layered on top of these other two utility services, called MyLoader. MyLoader takes XML objects and converts them into domain-specific objects, such as User, Product, or Contact.
The system needs to load all the contacts for the logged-in user and display them on-screen. It calls MyLoader.loadContacts(), which in turn, calls XMLLoader.load(“http://example.com/contacts.xml”) which in turn calls WebLoader.load(“http://example.com/contacts.xml”). The WebLoader.load call creates a new response object and stores it for when the result is loaded or an error occurs. Once created it returns the response object from the load method. The XMLLoader.load method receives the response object, adds a handler to it called stringToXML which takes a string, parses it, and returns and XML object, and then returns that response object from its load method. The MyLoader class receives the response object, adds its own handler, xmlToContacts, to format the XML into a list of Contact objects and returns the response. The system receives the response object and adds its own handler, displayContacts, to display the list of contacts on the screen.
When the load is completed, WebLoader calls complete(data) on the stored response object. The response object then loops through the handlers added to it, calling stringToXML first, receiving a value back from it, passing that value into xmlToContacts, receiving a value back from that, and passing a list of contacts into the displayContacts handler which doesn’t return anything, but displays the contacts.
Let’s add a cache to our MyLoader class. When the contact list is loaded, it stores the array of contacts in memory. Next time loadContacts is called, the MyLoader class creates a response object, calls complete(cachedContacts) on it, and returns it. Then, as soon as the caller adds displayContacts handler to the response, the response calls displayContacts and passes in the array of contacts which was cached. Thus, the caller doesn’t need to know whether the call has been cached or not, whether it is asynchronous or not.
While this may sound like a lot is going on, it is actually quite simple and allows service classes to be created and added to libraries for use in other systems. In the XMLLoader example, you don’t have to know or worry about how it gets the XML, the calling class can just know that it will receive XML in the response. The same goes for the MyLoader class. These services can be layered on top of each other like TCP and HTTP do, abstracting the higher levels from the lower levels.
The Response Pattern is something I made up. I created a Response class and as I’ve been using it more and more, it has turned out to be extremely useful and fun to use. I have found it much more preferable to using event listeners, Responder objects, and callbacks in Flash. I have wrapped the SQL classes in Adobe AIR with a service class that uses Reponse to make asynchronous database operations palatable. I have wrapped the File class for AIR. I have created a teeny tiny version of RemoteObject called RemoteService that uses Response. And I have created an ImageLibrary class which stores loaded images for me and resizes them to the size I need. Because it stores the images, sometimes calls are asynchronous and sometimes synchronous, but it is handled the same either way.
I plan to put together a library of often used services that are built around this concept for everyone to use. But until then, you can find a version of the Response class in the flight.net package of the Flight framework.
Update: I posted about the ActionScript implementation of the Response pattern. You can see how it is used and why it is different. It looks similar to other type of implementations, but if you ever use it you’ll see how different it is. It’s the littlest things in life that make the biggest differences.