Simplify code by using composition

Simplify code by using composition

In my post about software simplicity I talked about how we introduce complexity in software without realizing it. In this post I want to discuss a pattern that allows you to get rid of the complexity introduced by using annotations or aspect oriented concepts. Let’s take a look at a basic example:

This class has two small methods to create and update a task. Until now, this code is not complex and is fairly straightforward. Since these methods map one to one with use cases, they’re a good place to deal with cross-cutting concerns such as logging, authorization and exception handling.

Implementing cross-cutting concerns with annotations

A common solution is using annotations or aspect oriented programming. This allows us to describe how code behaves:

Apart from the attributes, the code looks equally simple and although it’s still as readable as it was before, it’s a lot more complicated. For those attributes to do their work, we need something to intercept calls to these methods. To intercept the calls, we need to make sure that anything that calls these methods, gets rewritten (either at compile time or dynamically at runtime).

This can be done by using a dynamic proxy (there are other ways, but none of them are really simple). A dynamic proxy is a runtime generated class, that inherits from your class and overrides its methods. In the overridden method, depending on the implementation of the aspect, it can do something before or after the original method or something entirely different than what the method was actually supposed to do. That’s a lot of hidden complexity:

A runtime generated class
That’s definitely not simple

Inherits from your class and overrides the method
This implies that you define your method as virtual. This is something you should just “know”

It can do something before or after the original method or something entirely different than what the method was actually supposed to do
From a client’s perspective, this is unexpected behavior. When you look at the CreateTask-method, at first sight there’s nothing to it: it creates and inserts a new Task, true to the name of the method. With the annotations, that’s not really true anymore: it now logs all calls (how? where? before? after?), ignores exceptions (all of them? does it log them?) and it authorizes the call (what happens when auth fails?).

So these three little annotations actually introduce a lot of complexity, while still keeping our code deceivingly easy.

The problem

Although the annotations introduced complexity, they still add value: we are not mixing cross-cutting concerns with domain logic. So is this complexity a necessary evil or is there a better solution? To find a better solution, we first need to find out what the problem is.

What we want to do is add behavior on top of an existing method. Why can’t we do that with plain composition? Do we need to use runtime generated code?

The problem is that we don’t have a** common interface**. If there was one, we could get rid of the dynamic proxies and annotations and just build an explicit object graph (using a decorator pattern for example). A dynamic proxy solves this by creating a common interface at runtime in the form of a PointCut. Because there’s no common interface at compile time, it generates one at runtime using reflection, so you can write an attribute implementation like this:

This method can now be attached to any other method. A dynamic proxy enables us to convert any type of method-signature into a PointCut. Or phrased differently: a dynamic proxy enables us to convert any method into a message.

Changing the problem

Solving this problem is impossible with simple composition (using today’s languages as far as I know). So, to come up with a simpler solution, we first need to change our problem. Instead of dealing with N different methods that have N parameters, we could say that we’re only going to have a method that has 1 parameter. To do so, we need to apply a small refactoring: “Introduce parameter object” (Fowler, Refactoring, improving the design of existing code). This is how our code looks like after applying the refactoring:

Instead of having methods with N parameters, each method now just accepts one parameter. That parameter is an object which holds all the parameters w epreviously had and implements an interface ICommand. (ICommand is just a **marker interface, **it doesn’t have any properties or methods).

The solution

Since all our methods are now the same, we can extract a common interface:

The LogHandler implements the same interface, (and thus can replace the original implementation). We accept another instance of the same interface which we’ll invoke after logging the message. This is a basic example of the decorator design pattern.

Single responsibility: when we apply annotations to a class, we’re violating SRP (single responsibility principle): even though it’s metadata, the class is still responsible for having the correct metadata about logging, exception handling, … If we extract this into a composeable solution like we did, we can now configure this behavior at a higher level and consolidate it.

Testability: using annotations, you’d have to run the application to test that your pipeline works as expected (that means end-to-end testing). With simple composition, you could write an integration test (in the form of a unit test) that creates the pipeline and verifies the results

Using this pattern is just one example of how we can keep our code simple and prevent complexity from creeping in. This is not easy though. Simple != easy, (often the opposite is true) but it’s well worth it in the long run. Rich Hickey has an excellent presentation on the difference between simple and easy which I recommend watching: http://www.infoq.com/presentations/Simple-Made-Easy