You are here: Home Articles A Java Component Architecture
Navigation
OpenID Log in

 

A Java Component Architecture

by Martin Aspeli last modified Nov 27, 2007 09:35 PM

Does such a thing exist?

Java isn't a bad language per se. At least not when it's paired with appropriate tools, such as Eclipse. I'm actually quite a fan of Java when it comes to middle-to-back-tier work of a certain size and complexity (both in terms of code and team structure). I'm not so convinced by the solutions I've seen for front-end web applications, but let's leave that to one side for now.

I am also a big fan of the Zope 3 Component Architecture and the design principles it espouses. The CA is loosely based on two well-known design patterns: Adapter and Singleton.

Briefly, the Adapter pattern is about providing small classes that adapt an object from one interface to another. This allows a model of design where value objects are as simple as possible, with minimalist interfaces. Various behaviour is then added using adapters that adapt these objects to a different interface. Here's a simple, not-quite-valid-syntax example:

class Receipt {

    private int amount;

    public void setAmount(int amount) {
        this.amount = amount;
    }

    public int getAmount() {
        return this.amount;
    }

}

class Ransom {

    private long cash;

    public void setCash(long cash) {
        this.cash = cash;
    }

    public long getCash() {
        return this.cash;
    }

}

interface IPayment {

    public void pay();    

}

class ReceiptPayment implements IPayment {

    private Receipt receipt;

    public ReceiptPayment(Receipt receipt) {
        this.receipt = receipt;
    }

    public void pay() {
        int amountToPay = receipt.getAmount();
        ... // perform payment operation here
    }

}

class RansomPayment implements IPayment {

    private Ransom ransom;

    public RansomPayment(Ransom ransom {
        this.ransom = ransom;
    }

    public void pay() {
        long amountToPay = ransom.getCash();
        ... // perform payment operation here
    }

}

 
The idea here is that if we need to use an IPayment on a Receipt object, we can create an instance of the ReceiptPayment adapter and then call the pay() method. Ditto using the RansomPayment adapter if we started with a Ransom object. The normal rules of polymorphism apply: once the adapter is created, general code can be written in terms of an IPayment and wouldn't care where the implementation came from.

Singletons should be more familiar. They are simply objects that are created once (either on application startup or on first access). The single instance is shared among all clients.

Now, the Zope 3 Component Architecture makes it easier to work with adapters and utilities -  a variant on the singleton pattern where instances are looked up by interface ("give me the object providing IRestaurantGuide") or optionally interface and name ("give me the object providing IRestaurant with name "Burger Queen" It does this by providing an adapter registry and a utility registry. It's the former that I miss the most.

The utility/singleton aspect is handled reasonably well by frameworks such as Spring. Spring lets us manage a registry of Java Beans, with a general factory to retrieve beans by name. Usefully, Spring also has inversion of control features that allow the precise composition of objects to be determined at run-time. That is, when I load an IRestaurantGuide service, it could require an IAddressLookup service, which it stores as a bean property. Through a Spring context configuration, the factory that ultimately returns the IRestaurantGuide will determine the appropriate implementation of its dependency IAddressLookup, and set this as a property on the IRestaurantGuide.

By contrast, I haven't yet found a useful alternative to the adapter registry in Java land. Adapters in Zope 3 are registered (using an XML syntax, most commonly) according to a few dimensions:

  • What interface will the resulting adapter implement (the provides/implements dimension).
  • What type of object(s) will the adapter adapt (the for/adapts dimension).
  • What is the name of this adapter (the name dimension). This is optional and only used when adapters are looked up by a specific name (in situations where there's more than one meaningful adapter for a particular context).

Let's use a made-up XML syntax to register the two adapters above:

<adapters>
    <adapter
        for="net.martinaspeli.blog.Receipt"
        provides="net.martinaspeli.blog.IPayment"
        factory="net.martinaspeli.blog.ReceiptPayment"
        />
    <adapter
        for="net.martinaspeli.blog.Ransom"
        provides="net.martinaspeli.blog.IPayment"
        factory="net.martinaspeli.blog.RansomPayment"
        />
</adapters>

Here is some pseudo-Java-code that illustrates how an adapter registry might be used in practice:

/** 
 * Whatever the context item is, pay it!
 */
public void makePayment(Object item) {
    IPayment payment = AdapterRegistry.adapt(IPayment.class, item);
    payment.pay();
} 

 

In this case, the adapt() method is looking for an unnamed uni-adapter (it only adapts a single context object, not a combination of objects) that can turn the context item (whatever it may be) into an IPayment. Looking at the registrations above, it should pick either RansomPayment (if item is a Ransom object) or ReceiptPayment (if item is a Receipt object). It will race an exception if it can't find a suitable adapter. Overloaded versions of adapt() would deal with named adapters (by taking a string parameter specifying the name) and multi-adapters (by taking a list of objects to adapt, rather than a single one).

The adapter registry in Zope 3 is quite clever. When determining the appropriate adapter it inspects its context (the item above) and compares it with the currently registered adapters, looking for the most specific match. For example, if the context implements multiple interfaces and there's an appropriate adapter for each, the registry will look at the most specific interface - the one implemented by the most concrete class (i.e. subclasses are preferred over base classes), using the most specific interface (i.e. sub-interfaces are preferred over base-interfaces). Essentially, this is just a search over an ordered tree of interfaces provided by the context against a registry of adapters that are known to the Component Architecture. There is also a special case: If the context object provides the required interface directly, there is no need to look up an adapter: the object itself is returned.

This style of programming has three key advantages:

  1. By factoring behaviour out into separate adapters, value objects (those that store persistent or transient state) are kept as simple as possible.
  2. Adding new behaviour simply means adding new adapters. General behaviour can be generalised into general adapters. This avoids the need to modify existing classes to add new types of behaviour. In the case of Java, this is doubly important, since Java does not support multiple inheritance. Thus, generalisable behaviour that needs to apply to multiple objects cannot be "mixed into" concrete classes by inheriting from multiple base classes (in any case, this type of design leads to very large class hierarchies and is probably best avoided).
  3. By using adapter lookups to generalise code, we gain a powerful method of re-use and customisation. Quite often, there will be one standard adapter that is registered for a very general object type. More specific adapters for more specific types (e.g. subclasses of the class that the general adapter was registered for) can be registered to provide specific behaviour, overriding the general implementation. The "client code" using the adapter is none the wiser.

In principle, creating a Component Architecture like this for Java should be simple. Using Java 5 Generics, the API can be made relatively natural (avoiding the need for extensive casting, say). The registrations of adapters would probably be externalised to an XML configuration file (yay - more of those!).The only hard part is the search algorithm that picks the right adapter, but that's mostly about grokking (pun intended) the Java introspection API in order to determine the sequence of interfaces (or concrete classes) to search for adapters for.

I'm hoping a kind reader will point out to me that this all exists somewhere. Or if it doesn't - create it. :-)

Document Actions

Eclipse has an adaptation registry

Posted by http://kteague.myopenid.com/ at Nov 27, 2007 11:43 PM
Reading this interview with Erich Gamma, a Design Pattern co-author the other day, this jumped out at me, "One example of this is the IAdaptable interface. Classes implementing this interface can be adapted to another interface. This is an example of the Extension Object pattern."

http://www.artima.com/lejava/articles/designprinciples.html

Lots more interesting reading in that article. Regardless of the specific patterns used (and what you call those patterns) the idea of "program to an interface, not an implementation" is a powerful one.

This describes adaption and an adapter registry as it's implemented in Eclipse:

http://wiki.eclipse.org/FAQ[…]able_and_IAdapterFactory%3F

This only appears to give you uni-adapters, and still requires you to embed the capability to perform adaption inside classes implementing IAdaptable. No idea if it's usable outside Eclipse either, but if it isn't I'm not going to create a JCA for you ... :-)


Plone Book
Professional Plone 4 Development

I am the author of a book called Professional Plone Development. You can read more about it here.

About this site

This Plone site is kindly hosted by: 

Six Feet Up