Aspect oriented with Decorator pattern and interception

There is no doubt that following SOLID principles is one of the key factors in OO design and programming in this paradigm that allows you to have a software that’s easy to maintain and extend over time. Being not an expert in OO, I always try to understand and apply those principles when I’m making a change or a new functionality. Especially the Single Responsibility Principle.

Having said that, there are some functionalities in every software that needs to be executed and that are not part of a particular module, assembly or package. Take for example, logging. Logging is essential for our systems because that’s the way we can see what’s happening in our application in production without the necessity to debug it. You can check logging files in order to watch a warning, info, error or whatever level of information you need to acquire.

In the case of caching, we could use it in our Data Access Layer (DAL), but the responsibility of a DAL class is to get the data, from whatever the source: database, file access, web service, etc. The caching is an add-on that can’t be in the specific method, because it break the single responsibility principle: if we want to exclude caching from our system we need to modify all of our classes and methods. A best approach would be to define the caching in one part, without touching the implementation.

Logging, security, caching, profiling, etc. are functionalities that spans the entire system. They are referred as Cross cutting Concerns or Aspect oriented programming (AOP), specific behaviours of our system that spans the system. They are everywhere but at the same time, they don’t belong to any part.

Fortunately we can use dependency injection to implement AOP, specifically with the Interceptor design pattern. But first, let’s see another pattern, the Structural Pattern Decorator. That way we can understand how the Interceptor pattern is implemented.

Decorator pattern

The decorator pattern attach additional responsibilities to an object dynamically. Their UML diagram is as follows.

Decorator

From the image we can see:

  • We have a Component interface or abstract class that declare the operation() method.
  • We have a ConcreteComponent that implements the Component interface/abstract class.
  • The Decorator is a class that implements the Component interface/abstract class and also has a Component private variable.

The idea is straightforward:

  • Instead of using ConcreteComponent we use Decorator, passing in their constructor the ConcreteComponent as parameter.
  • Decorator receives the ConcreteComponent and saves it in his private variable.
  • The operation() method in the Decorator executes the ConcreteComponent.operation() method and then (or before) execute his operation() implementation.
  • When the application executes the Component.operation() we are just talking of the operation. The internal implementation, and his extension, was made because we decorated the ConcreteComponent class.

Let’s work with a simple example: we have a simple 2D matrix of integers and we want to get the sum of each cell. We can have two implementations:

  1. Sum first by row.
  2. Sum first by column.

(As we’ve seen in a previous article, the Sum first by row is more efficient than with the Sum first by column.)

We want to measure the spent time on each algorithm, sending a message if the code takes more than five seconds. Certainly, we can add a simple timer on each implementation, as follow:

    
class ColumnFirstCalculator: ICalculator
{
    public long Calculate(int[,] matrix)
    {
        Stopwatch stopWatch = new Stopwatch();
        stopWatch.Start();

        // ... the class implementation

        stopWatch.Stop();
        TimeSpan ts = stopWatch.Elapsed;
        if(ts.TotalSeconds > 5)
        {
            Console.WriteLine("The method takes " + ts.TotalSeconds.ToString() + " seconds!");
        }
    }
}
class RowFirstCalculator: ICalculator
{
    public long Calculate(int[,] matrix)
    {
        Stopwatch stopWatch = new Stopwatch();
        stopWatch.Start();

        // ... the class implementation

        stopWatch.Stop();
        TimeSpan ts = stopWatch.Elapsed;
        if(ts.TotalSeconds > 5)
        {
            Console.WriteLine("The method takes " + ts.TotalSeconds.ToString() + " seconds!");
        }
    }
}
    

We added the time implementation, but now the Calculate() method has functionalities that does not belongs to the objective of the method. Worst if we have a lot of methods and classes that we want to measure.

Now we can try with the decorator pattern. We simply create a Decorator class as described in the UML diagram. The change now just affects the Program class:

    
ICalculator rowFirstTimer = new TimerDecorator(new RowFirstCalculator());
ICalculator columnFirstTimer = new TimerDecorator(new ColumnFirstCalculator());
long sumRowFirstTimer = rowFirstTimer.Calculate(matrix);
long sumColumnFirstTimer = columnFirstTimer.Calculate(matrix);

class TimerDecorator: ICalculator
{
    ICalculator _calculator;
    public TimerDecorator(ICalculator calculator)
    {
        _calculator = calculator;
    }

    public long Calculate(int[,] matrix)
    {
        // Here we start timer
        long sum = _calculator.Calculate(matrix);
        // Here we stop timer and warn if it takes too much time
        return sum;
    }
}       
    

How it’s used? Instead of calling the RowFirstCalculator or ColumnFirstCalculator directly, we encapsulate the call in the Decorator:

    
ICalculator rowFirstTimer = new TimerDecorator(new RowFirstCalculator());
ICalculator columnFirstTimer = new TimerDecorator(new ColumnFirstCalculator());
long sumRowFirstTimer = rowFirstTimer.Calculate(matrix);
long sumColumnFirstTimer = columnFirstTimer.Calculate(matrix);
    

The Decorator pattern adds a simple but effective way to extend the logic of our algorithms without the necessity to change our codebase. We just need to change the starting point. Because we worked with interfaces we can plug and change our specific implementations without much worry on the effects.

Interceptor pattern

The interceptor pattern is used in the same way as the decorator. The difference arises in how it’s implemented. In the case of interceptor we simply catch the method execution on runtime.

We can achieve this using the dependency injection pattern. Again, using the same example, we define a concrete implementation of the ICalculator interface using a dependency injection library, like Ninject:

    
static void Main(string[] args)
{
  var kernel = new StandardKernel();
  kernel.Bind().To().
    Named("Columns").Intercept().With();

  kernel.Bind().To().
    Named("Rows").Intercept().With();

  // Interceptor
  ICalculator rowFirst = kernel.Get("Columns");
  ICalculator columnFirst = kernel.Get("Rows");

  long sumRowFirstTimer = rowFirst.Calculate(matrix);
  long sumColumnFirstTimer = columnFirst.Calculate(matrix);
}

// TimerInterceptor
class TimerInterceptor: IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        // Stopwatch start timer
        ...

        // Here we execute the method
        invocation.Proceed();

        // Stopwatch stop timer and warning if exceeds threshold time
        ...
    }
}
    

Maybe you can think that there is no gain with this implementation. That’s because we just have a simple console application that defines and use in the same method the interface implementations, but if you use it in a bigger application you start to see the advantage:

  • With the dependency injection you define in one starting point all your dependences based on interface segregation.
  • In that same place you define the interception methods.

Another big advantage of interception is that it’s very generic. If you have a lot of classes that you want to extend, using the decorator pattern implies to

  • Create a decorator for every interface
  • Implement every method with the decorator logic.

In case of an interceptor it only needs one class.

Final thoughts

How we can use this techniques? we have a lot of options:

  • Logging of method calls
  • Logging of exceptions
  • Security concerns
  • Implementing caching for DAL or another method
  • Profiling

We can nest decorators/interceptors. In the source code of the repository the code has two decorators and two interceptors: one for timing and another for unhandled exception. The nesting is very easy:

    
// Nesting with Decorator
ICalculator rowFirstTimer = new TimerDecorator(new ExceptionDecorator(new RowFirstCalculator()));
ICalculator columnFirstTimer = new TimerDecorator(new ExceptionDecorator(new ColumnFirstCalculator()));

// Nesting with Interception (Ninject)
var columCalculator = kernel.Bind().To().Named("Columns");
columCalculator.Intercept().With().InOrder(1);
columCalculator.Intercept().With().InOrder(2);

var rowCalculator = kernel.Bind().To().Named("Rows");
rowCalculator.Intercept().With().InOrder(1);
rowCalculator.Intercept().With().InOrder(2);
    

The good part is that we can use interception or decorator to extend our methods, without affecting the original logic. For example, for profiling: we are measuring certain methods in production. When we get any delay that exceeds certain amount of time we simply make a record on the logging system. That way we can start to see where we have to work in order to have a more efficient code.

Greetings!

Source code