tags:

views:

208

answers:

1

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?

+6  A: 

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.

Mark Seemann
I've considered decorator as well. I suppose if I keep my methods concise enough then I won't be sacrificing the granularity of control I might expect from the example solutions I proposed. Is there any reason you would prefer Decorator? Because the decorated class needs no knowledge of the cross-cutting concerns?
Stacy Vicknair
Is this your preferred solution to cross-cutting concerns when you handle them?
Stacy Vicknair
I prefer Decorator over injection of Strategies because it keeps the real implementation squeky clean. I often prefer that, but may occasionally also use Interception as provided by e.g. Castle Windsor.
Mark Seemann
Instead of this.innerThing(s), should it be this.innerThing.DoStuff(s)?
Andrew
@Andrew: Yes, you are correct. Thank you. Corrected.
Mark Seemann
@Mark - Wow, you're quick!
Andrew