Tapestry Training -- From The Source

Let me help you get your team up to speed in Tapestry ... fast. Visit howardlewisship.com for details on training, mentoring and support!

Friday, November 05, 2004

HiveMind AdapterRegistryFactory

Just did a little burst of work on HiveMind and added the AdapterRegistryFactory. What's it all about?

It's an implementation of the adapter pattern. The idea is that you have some common operation that should apply to all sorts of different types; perhaps its code that is used to output an XML representation of different objects, or performs some other common operation. In Tapestry, an example of this is the way different objects are evaluated as conditions for the Conditional component: a java.lang.Boolean is obvious, but others require some work: java.lang.String if it contains non-whitespace character; any Number if the value is non-zero, any java.util.Collection if not empty, etc.

With an AdapterRegistry, you define an interface for your adapters. The first parameter of each service method will be used to select the adapter.

public interface ConditionEvaluator
{
  public boolean evaluate(Object value);
}
This idiom reflects that the adaptors are singletons into which the object to operate upon is provided as a method parameter. This differs somewhat from the Gang Of Four useage, where a specific adaptor instance is created for a specific object instance.
You define a configuration and contribute classes and adapters:
<contribution configuration-id="ConditionEvaluators">
  <adapter class="java.lang.Boolean" object="instance:BooleanAdapter"/>
  <adapter class="java.lang.Number" object="instance:NumberAdapter"/>
  . . .

Your adapter implementations are simple:

public class BooleanAdapter implements ConditionEvaluator
{
  public boolean evaluate(Object value)
  {
    Boolean b = (Boolean)value;
 
    return b.booleanValue();
  }
}

Lastly, you define your service point:

<service-point id="ConditionEvaluator" interface="ConditionEvaluator">
  <invoke-factory service-id="hivemind.lib.AdapterRegistryFactory">
    <construct configuration-id="ConditionEvaluators"/>
  </invoke-factory>
</service-point>

At this point, you can reference this service and invoke methods on it, passing different instances into it. Internally, the service implementation will locate the matching adapter (BooleanAdapter for java.lang.Boolean, NumberAdapter for java.lang.Integer and friends) and let the adapter do the work. What's powerful is that your code just sees the one service proxy ... HiveMind connects the dots to get the correct adapter implementation invoked when you invoke a method on the proxy. This is similar to how the threaded and pooled service models expose just the one proxy. There are no questions such as "is this service threaded?" or "is this service an adapter?" ... it all just works.

For the moment, you are not allowed to pass null (you'll get a runtime exception). Still pondering the right approach to handling nulls.

Much like the PipelineFactory, this is a quick way to assemble a pretty sophisticated apparatus ... that all hides behind a single service and a single interface. I often talk about HiveMind's power coming from the mix of services and configurations. In a way that is familiar to Lisp hackers and the like, code (in HiveMind terms, services) gets all mixed up with data (configurations) to allow elegant, powerful solutions to arise.

Updated: Changed the naming from "Adaptor" to "Adapter".

No comments: