Monday, January 7, 2013

Variation of "State" Design Pattern in Java

This post is about a variation of “State” design pattern implemented in Java. I used similar approach in one of the projects I’ve been working on and I found it pretty useful. I assume you are familiar with “State” design pattern. I would suggest reading this introduction if this is not the case. Alternatively you can learn about the pattern from the famous GoF book.

Ok, let’s start. Consider the following scenario: you are working on a software system which should integrate at certain points with another system using SOAP web-services. Workflow of your software system highly depends on this integration, but the external system may be occasionally unavailable or require a long time to respond. This can significantly slow down your development activities.

If you know the expected outcome of the external system then you can simulate it locally during development phase without actual invocation. One of possible approaches is to put all the integration logic in a separate component and make the rest of your system talk to the external system via that component exclusively. The integration component in its turn can be configured to either invoke the real external system or to simulate locally its expected outcome. Let’s look at some code.

The integration component interface might look like this:
public interface ExternalSystemClient {

    AppResult invokeExternalSystem(AppParams params);

}
For simplicity it contains just a single method which invokes an external system and delivers its outcome. AppParams and AppResult classes represent invocation parameters and invocation result respectively in terms of application. Integration component’s responsibility is to convert them to the form understandable by external system (WebServiceParams and WebServiceResult introduced in the next listing). Implementation of integration component might look like this:
public class ExternalSystemClientImpl implements ExternalSystemClient {
    private ClientActions clientActions;

    public ExternalSystemClientImpl(boolean isExternalSystemEnabled) {
        clientActions = isExternalSystemEnabled ?
                new ExternalSystemEnabled() : 
                new ExternalSystemDisabled();
    }

    private interface ClientActions {
        WebServiceResult invokeExternalSystem(
                WebServiceParams webParams);
    }

    private class ExternalSystemEnabled implements ClientActions {
        @Override
        public WebServiceResult invokeExternalSystem(
                WebServiceParams webParams) {
            // invoke external system and return the results
        }
    }

    private class ExternalSystemDisabled implements ClientActions {
        @Override
        public WebServiceResult invokeExternalSystem(
                WebServiceParams webParams) {
            // simulate external system outcome
        }
    }
    @Override
    public AppResult invokeExternalSystem(AppParams params) {
        // validate input parameters
        // ...
        WebServiceParams webParams = new WebServiceParams();
        // convert AppParams to WebServiceParams
        // ...
        WebServiceResult invocationResult =
                clientActions.invokeExternalSystem(webParams);

        AppResult result = new AppResult();
        // convert WebServiceResult to AppResult
        // ...
        return result;
    }
}
Let’s break down this listing into individual parts. Class ExternalSystemClientImpl implements ExternalSystemClient interface to provide integration with external system. This class contains one private interface – ClientActions. This interface helps to factor out the code which directly depends on whether external system invocation should be performed or not. Hence there are two implementations of ClientActions interface: ExternalSystemEnabled (which do performs external system invocation) and ExternalSystemDisabled (which is intended solely for development purposes and simulates the invocation locally).

Interface ClientActions plus classes ExternalSystemEnabled and ExternalSystemDisabled effectively form the crux of the “State” design pattern. A certain state is selected in the ExternalSystemClientImpl constructor depending on the value of isExternalSystemEnabled parameter. Method invokeExternalSystem() shows “State” design pattern in action. This method has the following responsibilities:

  • Validate input parameters.
  • Convert AppParams instance to the form understandable by the external system (i.e. convert AppParams instance to WebServiceParams instance).
  • Invoke the external system via web-service.
  • Convert WebServiceResult to the form understandable by our application (i.e. convert WebServiceResult to AppResult) and return it.

As you can see the implementation of this method is completely independent of whether the external system is actually invoked or its outcome is locally generated. The more methods you have in ExternalSystemClient interface, the greater benefit you gain from “State” design pattern application.

Alternative Techniques

Let’s look at some alternatives. The first and the most obvious one is to simply check the flag in the ExternalSystemClientImpl#invokeExternalSystem() method and act accordingly depending on the result. The implementation might look like this:
@Override
public AppResult invokeExternalSystem(AppParams params) {
    // validate input parameters
    // ...
    WebServiceParams webParams = new WebServiceParams();
    // convert AppParams to WebServiceParams
    // ...

    WebServiceResult invocationResult;

    if (isExternalSystemEnabled) {
        invocationResult = // ... invoke actual external system
    } else {
        invocationResult = // ... simulate invocation result locally
    }

    AppResult result = new AppResult();
    // convert WebServiceResult to AppResult
    // ...

    return result;
}
This is not an option to be honest. The more methods you have in ExternalSystemClient interface, the more headaches you get during maintenance due to code duplication. Assume you have 15 methods and you need to introduce the third state. If you stick to the aforementioned approach then you will have to revisit all these methods to add new state handling. Code duplication is always error-prone and should be avoided. “State” design pattern works much better in this case because all you need to do is to introduce another ClientActions interface implementation and slightly modify the ExternalSystemClientImpl constructor.

One viable alternative is to provide ClientActions implementation dependency directly via constructor parameter. In this case the required object can be easily injected using Spring, Guice or any other dependency injection container. Method invokeExternalSystem() will be implemented similarly to the initial “State” design pattern description:
@Override
public AppResult invokeExternalSystem(AppParams params) {
    // validate input parameters
    // ...
    WebServiceParams webParams = new WebServiceParams();
    // convert AppParams to WebServiceParams
    // ...
    WebServiceResult invocationResult =
            clientActions.invokeExternalSystem(webParams);

    AppResult result = new AppResult();
    // convert WebServiceResult to AppResult
    // ...

    return result;
}
The constructor of ExternalSystemClientImpl class will be slightly changed:
public ExternalSystemClientImpl(ClientActions clientActions) {
    this.clientActions = clientActions;
}
This solution works fine and provides the same benefits as the initial one. But in this case ClientActions interface and its implementations cannot be kept private to ExternalSystemClientImpl class anymore. Sometimes this is exactly what we need (for example if ClientActions is a full-fledged component that is injected in several other components in the system), but in this particular case ClientActions, ExternalSystemEnabled and ExternalSystemDisabled are just implementation details, not the components on their own. Hence they should be kept as private as possible.

Thanks for reading,
See you soon!

No comments:

Post a Comment