The Designer’s Block – Dynamic Proxies, Instrumentation…?

Caught your attention with the fancy words in the title, didn’t I?  We ended the last post with a question:

“So how do we keep the API of MyStashService intuitive while also maintaining all the beautiful work and principles we have been applying to it so far in previous posts?”

There are many ways to solution this, but first at the outset, lets agree that based on the current state of affairs with regards to the consumer of the MyStashService – the client programs – we want to expose the initial “facade” of explicit methods like “credit”, “debit”, etc which are easy and intuitive to use and worry about the implementation later.  Lets establish such a contract (read “Interface”) with the clients.  So how do we do that in a standard OO language like Java/C-hash/etc?  Yes, an interface.  So lets go ahead and put in a IMyStashService interface – I am quite wedded to the notation of using a prefix of “I” for an interface, although in modern IDEs of today, it is unnecessary as the IDE does annotate interfaces with a nice symbol of some sort.  So:

public interface IMyStashService {

    void credit(CreditInfo credit);

    void debit(DebitInfo debit);

    double fetchBalance();
}

Pictorially, what I want to achieve is this:

DynamicProxies

We need to implement IMyStashService, such that the implementation directly wires it to the “doUsecase()” method of the appropriate service object instance.  Please note – the following prose, explains an approach that I will probably NOT take yet, but is presented more for demonstrating the level of lateral thinking you should be willing to do so as to achieve the best of all worlds – never give up, google the problem you are facing, you are not the only person addressing such design challenges.

If you have been programming Java for a while, the following idea may have popped into your head – “I know! I can use dynamic proxies” – using the java.lang.reflect.Proxy.  This, in my opinion, is a very useful class tucked away, which many programmers are probably not aware of!

Here is a basic implementation of MyStashService using dynamic proxies:

public class MyStashService implements InvocationHandler {

    private MyStashServiceMapper myStashServiceMapper;

    MyStashService(MyStashDB myStashDB) {

        this.myStashServiceMapper = 
                new MyStashServiceMapper(myStashDB);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] input) 
            throws Throwable {

        return myStashServiceMapper.getService(
                method.getName()).doUsecase(input);
    }
}

Notice that MyStashService does not directly implement IMyStashService.  It relies on java.lang.reflect.Proxy to generate an implementation on-the-fly, doing the necessary routing of calls on IMystashService to the “InvocationHandler” shown above. This in-turn uses the name of the method and the MyStashServiceMapper to figure out which service instance to call.

But what if the method name changes?  That would be an issue, as the “mapper” which relies on the method name for retrieval of the right service, would start failing, unless the name change is also done and accounted for in the MyStashServiceMapper class.

You could improve the above by using the java 1.5 feature – annotations (see below) and enums.  When you change from “String(s)” to “Enum(s)”, you will notice that I have eliminated the “mapper” class totally by making the enum itself the factory for the service instances.  So:

public interface IMyStashService {

    @UsecaseName(value = Usecase.CREDIT)
    void credit(CreditInfo credit);

    @UsecaseName(value = Usecase.DEBIT)
    void debit(DebitInfo debit);

    @UsecaseName(value = Usecase.FETCH_BALANCE)
    double fetchBalance();
}

@Retention(value = RetentionPolicy.RUNTIME)
public @interface UsecaseName {

    Usecase value();
}

public class MyStashService implements InvocationHandler {

    private MyStashDB myStashDB;

    MyStashService(MyStashDB myStashDB) {

        this.myStashDB = myStashDB;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) 
        throws Throwable {

        // Interpret UsecaseName Name from Annotation
        Usecase usecase = method.getAnnotationsByType(
                UsecaseName.class)[0].value();

        // Get Service for UsecaseName
        IService service = usecase.getService(myStashDB);

        // Handle No Argument Scenario
        Object input = toInputFromArgs(args);

        // Execute UsecaseName
        return service.doUsecase(input);
    }

    @Nullable
    private Object toInputFromArgs(Object[] args) {

        return ((args != null && args.length > 0) ? args[0] : null);
    }
}

One thing I like about this approach – aside from solving the problem we are trying to solve, it also gives us that much needed central block of code through which all use-cases go through – kind of like the ServletFilter chain in JEE.  This is very useful in implementing cross-cutting concerns like security checks, boundary exception handling, some kind of audit log, logging itself, etc

So now you have implemented an option which allows you to expose an explicit intuitive API to the clients while maintaining OO best practices internally.  There are, am sure, fancier solutions using technologies like “instrumentation” and “code generation” for achieving the same.

As I said in the beginning, am not sure if I will follow this design yet….am still deciding whether to depend on a Java feature like the “Proxy” to solve this for my simple application – may be I can strike a deal with the client programmers – ME 😉

Leave a comment