An Adapter normally wraps another object, so that it can be used in an interface it wasn't designed for, e.g., when you want to use

interface Node {
    Node parent();
    Iterable<Node> children();

together with

class TreeModel {
    private Node root;
    // example method (stupid)
    Node grandparent(Node node) {
        return node.parent().parent();

and you're given a class like

class File {
    File getParent() {...}
    File[] listFiles() {...}

you need to write some FileToNodeAdapter.

Unfortunately, it means that you need to wrap each single object and you also need both a way to get from FileToNodeAdapter to File (which is trivial, since it's embedded), but also from File to FileToNodeAdapter, which leads either to creating a new object each time or to using some Map, which must be either globally accessible or referenced in each FileToNodeAdapter.

The Pattern

Replace the interface Node by

interface NodeWorker<T> {
    T parentOf(T node);
    Iterable<T> childrenOf(T node);

and modify the TreeModel like

class TreeModel<T> {
    private NodeWorker<T> nodeWorker;
    private T root;
    // example method (stupid)
    T grandparent(T node) {
        return nodeWorker.parentOf(nodeWorker.parentOf(node));

Does this pattern have a name?

Are there any disadvantages, besides the fact that it is little bit more verbose and only applicable when you are in charge of the TreeModel code?

  • FileToNodeAdapter to File and back - the issues you describe remind of double dispatch - did you consider Visitor pattern to handle these? – gnat Oct 10 '11 at 12:54


It is the strategy pattern as mentioned earlier by Cameron MacFarland. But I also have a few problems with your question.

Your node interface is not using generics. Therefore, to use a Node you need to know what type of Node you were trying to use (File in your example). So if we fix this by adding Node then your two example becomes more clear.

Now what we see is that both are very similar with the exception that the Node one is stateful vs stateless as in the NodeWorker one.

You can see this clearly by adding a very quick: T getAdapted() to your Node interface.

Now you basically have the same code as the NodeWorker with one extra line of code but a slightly (arguably) cleaner grandParent method.

As far as the number of classes you need to implement using this new Node vs your NodeWorker? It's exactly the same now.

So now the question becomes a multi part question. If you want an adapter then you should never really know what the adapter was adapting (ideally). Because the adapter is your 'standard' way to interact with multiple subsystems that you don't want to have to manage. This mean you shouldn't ever KNOW what type of object was actually adapted. What did you want to do with the File? Would what you want to do with the File change if you were using some other type of Node? If not then add the interaction methods that you need to the Node interface.

What is the advantage of that? The advantage of that is that Node does NOT need generic types and can actually return other Node types WITHOUT having to let the clients know or even care.

For example suppose that we have File types Directory types and Disc types.

the grandParent call could conceivably return Directory as the parent of a file and Disc as the parent of the Directory which in essence would return DirectoryNodeAdapter and DiscNodeAdapter all under the interface of Node without the client having to worry about it!

Common methods (let's say delete) could be added to the interface instead and again you never have to worry about the fact that it was a Directory or Disc or File to delete as you Node adaptors will abstract that away from you.

Your strategy NodeWorker will not have this ability as is now typed to the first type File and the grandparent call assumes that this same type will be returned (This can be changed in your strategy interface of course).

Basically what it boils down to is your use case. I personally like to use the strategy pattern when working with the SAME set of items but the specifics of how I want to deal with it while performing the SAME tasks will vary.

A good example of this would be the Comparator interface in java where I know I'm dealing with a set of objects and know that I need to vary (or may change) how I compare them.

I personally use adapter patterns when I want to hide the underlying system should I want to swap out the underlying system at some point.

A good example of this is java jdbc and eclipse's plugin architecture.


Sounds like it's the Strategy Pattern.

The real-world example given at dofactory seems to fit your example the best.


I don't think there's a specific name for this; I think it's kind of a glorified adaptor using composition, but made very generic. Which is really powerful.

With NodeWorker and TreeModel, you're trading extension for composition, using generics. You're using generics to parameterize the underlying type of the nodes. So that any object type can just drop into the tree without modification.

There's one step you need, though, and that's the concrete implementations of NodeWorker, which might be based on concrete types of T. (Or you could just make NodeWorker a class, provide a get() method, and carry T object along without knowing or caring what it really is.)

FWIW In Haskell, there's this idea of "lifting" functions into a monadic space, and you're moving in that direction. You're putting objects of type T into the TreeModel space. TreeModel is basically on the road to becoming a monad. But that topic gets pretty deep and I always get it wrong.

EDIT: For example (pseudocode)

*class* NodeWorker<T> {
    T object;
    T get() { return object; }
    Nodeworker(T object) { this.object = object; }
    ... other parent/child information ...
    T parentOf(T node) { .... }
    Iterable<T> childrenOf(T node) { ... }

The advantage is that you just have one nodeworker that carries an object of arbitrary type. The disadvantage is that you don't know anything about object, and can't invoke its methods (except for the basic Object stuff).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.