views:

74

answers:

2

What is the best way to implement an interface that combines some instances of the same interface in various specified ways? I need to do this for multiple interfaces and I want to minimize the boilerplate and still achieve good efficiency, because I need this for a critical production system.

Here is a sketch of the problem.

Abstractly, I have a generic combiner class which takes the instances and specify the various combinators:

class Combiner<I> {
   I[] instances;

   <T> T combineSomeWay(InstanceMethod<I,T> method) {
     // ... method.call(instances[i]) ... combined in some way ...
   }

   // more combinators
}

Now, let's say I want to implement the following interface among many others:

Interface Foo {
  String bar(int baz);
}

I want to end up with code like this:

class FooCombiner implements Foo {
  Combiner<Foo> combiner;

  @Override 
  public String bar(final int baz) {
    return combiner.combineSomeWay(new InstanceMethod<Foo, String> {
      @Override public call(Foo instance) { return instance.bar(baz); } 
    });
  }
}

Now, this can quickly get long and winded if the interfaces have lots of methods. I know I could use a dynamic proxy from the Java reflection API to implement such interfaces, but method access via reflection is hundred times slower. So what are the alternatives to boilerplate and reflection in this case?

+2  A: 

I would have suggested dynamic proxies - is it really so much slower than a regular method call these days - I've heard that reflection does quite a bit of magic under the covers to speed repeated method calls. (And if it is 100x slower, are you sure you will you notice? Ok, just re-read your question - you'll notice!)

Otherwise, you basically have the solution in your question: Use a Command object to wrap each method in your interface. You can then pass each instance in the collection of interfaces to the command object for processing.

Of course, if you're feeling brave and adventurous, you could generate the implementation of your command objects, and the implementation of the combiner interface using dynamic class generation, with cglib, javassist, orther dynamic bytecode generator. That would avoid the boilerplate.

You may also have some success with aspects, particularly aspectJ with compile-time or load-time weaving, so you avoid reflection overhead. Sorry I can't give details.

mdma
I ended up using dynamic proxies, and it was much cleaner. Also, the slow down is not noticeable because method access if fast anyways.
namin
+1  A: 

You can reverse your combiners:

@Override
public String bar(int baz)
{
    //for (Foo f:combiner.combineSomeWay())// returns Iterator<Foo>
    for (Foo f:combiner) //combiner must implement Iterable<Foo> or Iterator<Foo>
    {
        // In case of several ways to combine
        // add() method should call some temp object
        // in combiner created (or installed) by 
        // combineSomeWay.
        // The best temp object to use is Iterator
        // returned by combiner.combineSomeWay();
        combiner.add(f.bar(baz));// or addResult, or addCallResult
    }
    // clear (or uninstall) temp object and result
    // thats why get* method 
    // name here is bad.
    return combiner.done(); 
}

Not a one-liner but easier to understand. That would be more complex if your methods throw exceptions though. You will need try/catch block and addException method.

Ha
Not a bad idea, but it won't work in my case, as I want the combiner to decide whether to call the method at all (for example, one combiner is to just call the method of the first instance and ignore the others). Thanks, anyways, for a nice twist.
namin
This is exactly why I use iterator here instead of collection. Combiner must provide objects through iterator when it needs to call the method and it can decide what next element the Iterator will return based on argument of add method. The continuations would simplify combiners a lot but java doesn't support them yet.
Ha
Ah, this is clever... In any case, it is still very boilerplaty to have a for loop in each method. But nice trick.
namin