What are the different methods for injecting cross-cutting concerns?

Stacy Vicknair picture Stacy Vicknair · Nov 10, 2009 · Viewed 7.8k times · Source

What are the different methods for injecting cross-cutting concerns into a class so that I can minimize the coupling of the classes involved while keeping the code testable (TDD or otherwise)?

For example, consider if I have a class that requires both logging functionality and centralized exception management. Should I use DIP and inject both required concerns via an interface into the class that requires them? Should I use a service locater that I pass to each class that will require some cross cutting functionality? Is there a different solution altogether? Am I asking the wrong question entirely?

Answer

Mark Seemann picture Mark Seemann · Nov 10, 2009

The Decorator design pattern is an excellent starting point for implementing cross-cutting concerns.

First you need to define an interface that models the service in question. Then you implement the real functionality of that service without thinking about your cross-cutting concern at all.

Then you can subsequently implement decorating classes that wrap around other instances and implement the desired cross-cutting concern.

This approach can be implemented entirely with Plain Old C# Objects (POCOs) and requires no extra frameworks.

However, if you get tired of writing all the extra decorators, you may want to use a framework. I have no experience with explicit AOP frameworks, but most DI Containers such as Castle Windsor offer AOP-like features.


Here's an example of using Decorators. Let's say that you have the following interface:

public interface IMyInterface
{
    void DoStuff(string s);
}

Your concrete implementation may do something very interesting, such as writing the string to the Console:

public class ConsoleThing : IMyInterface
{
    public void DoStuff(string s)
    {
        Console.WriteLine(s);
    }
}

If you wish to log the DoStuff operation, you can now implement a logging Decorator:

public class LoggingThing : IMyInterface
{
    private readonly IMyInterface innerThing;

    public LoggingThing(IMyInterface innerThing)
    {
        this.innerThing = innerThing;
    }

    public void DoStuff(string s)
    {
        this.innerThing.DoStuff(s);
        Log.Write("DoStuff", s);
    }
}

You can keep writing new Decorators, like a caching Decorator or one that implements security and so on, and just wrap them around each other.

Note: I rarely recommend static interfaces, so the Log.Write interface is not a recommendation, but merely mean as a placeholder. In a real implemetation, I'd inject some kind of ILogger interface into LoggingThing.