Showing posts with label instanceof. Show all posts
Showing posts with label instanceof. Show all posts

@since Wednesday, February 16, 2011

Strategy pattern as replacement to instanceof if statements

@throws 0 exception(s)
Let's begin with a simple rule: instanceof is an ugly pattern.



To add to this ugly pattern you might not notice a fault in such pattern (what if Bar extends Foo?) and the maintenance for it can go sky high as your system evolves. You will end up adding more ugly code and keep trying to rearrange those switches so that class hierarchy wouldn't collide with your logic.

The literature tells us that this type of design can be replaced by using the visitor pattern and make each class responsible on how it should be invoked by others. To me, that sounds awkward to force my domain objects to be familiar on their interaction with services, DAOs or other delegating classes. I prefer the use of strategy pattern and leave my code clean from coupling. The following is a slightly modified strategy pattern with a corresponding holder which enables re-use of strategies in a Dependency Injection environment. The very basic interface might looks as follows:



For the re-use part, we hold our concrete strategies in a holder in a simple Map (HashMap in the following example). If you're using some dependency injection framework you can inject all known implementation to the holder below and then inject the holder to clients use code.



To quickly go over the abstract implementation: the map holds our strategies by their declared discriminator/key. The holders uses generics to support using the holder with any extended type of strategy without casting. Another point to mention is the abstract method getDefaultStrategy() which enables our concrete implementation to use a default strategy (obvious) or throw some state exception if no strategy was found. You can simplify this implementation by using interfaces but with this added generic flexibility I managed to replace different strategies with the same basic holder abstraction.

The following is an example on how the holder and strategy above can be used in real life scenario.



As you can see, ExampleStrategy is simply extending the Strategy interface and provide a single execute() method. By injecting all known implementation (your IoC container should do it for you) and passing them to our AbstractStrategyHolder we're ready to use our strategies in our designated flows. Small note: I wouldn't recommend using the class type as discriminator but sometimes it's our best option.

We managed to clear our ugly instanceof switch with a single call to a strategy holder and invoke the matching strategy - cleaner code, easy maintenance. Need another reason why this is better? Testability! With this design implementation we can better test our code and write small, individual tests to our flows. Need another?! Extensibility! Need new strategy to perform another action? Add a new strategy and let our IoC container inject it to our current holder implementation (I've seen this happen so many times I believe that alone should convince you to use the strategy pattern).

The code examples (and for the builder example in earlier posts) can be found here.