views:

74

answers:

4

In my current project we have a couple of data classes that deal with core concepts of our application domain. Now at some places in our project we have to have different behavior depending on the concrete object in hand. E.g. a JList has the task to render a list of objects but we want the rendering to be slightly different, depending on the object's class. E.g. an object of class A should be rendered different than one of class B and class C is a totally different animal, too.

We encapsulate the behavior in strategy classes and then have a factory that returns a class suitable for the object that is to be rendered. From the client perspective, that is okay, I guess.

Now from the perspective of the factory this gets pretty ugly, because all we could come up with so far is stuff like

if (obj instanceof classA) return strategyA;
else if (obj instanceof classB) return strategyB;
...

Now, for a pool of already instantiated objects, a map would also work. But if the factory has to actually create a new object, we'd have to put another layer of factory/strategy objects into that map that then return a suitable strategies for displaying.

Is there any design pattern that deals nicely with this kind of problem?

+1  A: 

One way to do this is to delegate the implementation to the object itself. For instance, if classes A, B, and C are all rendered differently, you might have them each implement an interface such as:

interface IRenderable {
    public void render();
}

Then, each one would provide its own implementation of render(). To render a List of IRenderable, you would only need to iterate over its members and call the render() method of each.

Using this approach, you never have to explicitly check the type of an object. This is particularly useful if any of your classes are ever subclassed. Suppose you had class classD which extends classA, and was to be rendered differently from A. Now code like:

if (obj instanceof classA) return strategyA;
...
else if (obj instanceof classD) return strategyD;

will fail - you would always need to check in order of most to least specific. Better not to have to think about such things.

Edit: in response to your comment - if your goal is to keep the front end code out of the model objects, but you still want to avoid explicit checks, you can use the visitor pattern.

Something like this:

class Renderer {
    public void visit(classA obj);
    public void visit(classB obj);
    // etc
}

and

class classA {
    public void accept(Renderer r) {
        r.visit(this);
    }
}

Now, all the rendering code goes into the Renderer, and the model objects choose which method to call.

danben
Thanks for answering so fast. Your idea is interesting but has the disadvantage that the data objects, that should be just the model, gather lots of front-end code. This code has the usual problems with portability, code-size, testing and so on.
xmjx
A: 

You can have your model classes implement an interface like:

public interface RenderingStrategyProvider {
    public RenderingStrategy getRenderingStrategy();
}

and return an instance of the appropriate strategy. Like:

public ClassA implements RenderingStrategyProvider {
    public RenderingStrategy getRenderingStrategy() {
        return new ClassARenderingStrategy(this); 
        // or without this, depending on your other code
    }
}

In that case you wouldn't even need a factory. Or if you want such, it will contain just a one method call. That way you don't have the presentation logic inside your model classes.

Alternatively, you can use convention + reflection, but this is a weird. The strategy for a model class would be ModelClassStrategy, and you can have:

public RenderingStrategy createRenderingStrategy(Object modelObject) {
    return (RenderingStrategy) Class.forName(
          modelObject.getClass().getName() + "Strategy").newInstance();
}
Bozho
+1  A: 

Instead of the if/else block you can have a Factory interface, like this

interface RendererFactory {
   supports(Object obj);
   createRenderer(Object obj);
}

Then you can have an implementation which asks a list of other implementations if one of them support a given type. The other implementations may do an instanceof check in the supports method. The consumer of the renderer only needs to call createRenderer.

Advantage: Configuration of your RendererFactories possible

Disadvantage: You have to take care about the order of the RendererFactories (but you have to do that with if/else too)

Johannes B
+1. Similar to the Adaptable pattern and how Eclipse is using it for rendering elements in a UI.
mhaller
+1  A: 

I like the factory-serving-strategy objects a lot. But I wonder if you could treat it like IoC and register strategies for specifci types? You don't have a bunch of if-else's but you would have to 'register' them. But it might also be nice for testing - rather an implementing a 'mock factory' you'd register 'mock strategies'?

n8wrl