AOP vs decorator

March 28, 2012 mgroves 0 Comments
Tags: patterns decorator DynamicProxy

I came across this question about interception vs injection on Programmer's Stack Exchange. Since I'm always blogging and talking about AOP, my thoughts immediately went to suggesting an AOP framework.

However, Mark Seeman wrote a very good answer about using the decorator pattern (or proxy pattern depending on how you look at it), and beyond being a good answer, it made me pause and think about the implications. If decorator/proxy combined with dependency injection does the job, why would I ever introduce an AOP tool?

Well, in many cases the answer is: I shouldn't! If I'm only applying cross-cutting concerns to a handful of classes, it's totally feasible for me to write out proxy/decorators and wire them up in my IoC container. Consider:

  • 2 service interfaces: ICustomerRepository and IProductRepository
  • 2 implementations: CustomerRepository and ProductRepository (which implement the interfaces respectively)
  • 2 logging decorators: CustomerLoggingDecorator and ProductLoggingDecorator (which also implement the interfaces respectively)
  • 2 caching decorators: CustomerCachingDecorator and ProductCachingDecorator (which also implement the interfaces respectively)

And there you go. No post-compile step, no runtime reflection generation, just you, 8 classes/interfaces and your IoC tool of choice. The logging and caching decorator classes could be very similar in their implementations, but it's not so bad, and I still get a nice separation of concerns, and all my implementations are single responsibility.

But now consider:

  • 100 service interfaces: ICustomerRepository, IProductRepository, and 98 others...
  • 100 service implementations: CustomerRepository, ProductRepository, and 98 others...
  • 100 logging decorators: CustomerLoggingDecorator, ProductLoggingDecorator, and 98 others...
  • 100 caching decorators: CustomerCachingDecorator, ProductCachingDecorator, and 98 others...

Whoa. Well, I still have a very nice separation of concerns, and I'm still following single responsibility. Wiring them up in a good IoC tool shouldn't be a problem, so long as I follow a good naming convention. So this isn't terrible.

But, now I have 400 classes/interfaces, most of which probably have very similar boilerplate code throughout. If I make a change to that boilerplate in one class, I have to change it in (possibly) 99 other classes. I still think this architecture is better than just tangling logging/caching code inside of the service implementations, it still leaves a lot to be desired.

Now consider an architecture using, say, Castle DynamicProxy:

  • 100 service interfaces: ICustomerRepository, IProductRepository, and 98 others...
  • 100 service implementations: CustomerRepository, ProductRepository, and 98 others...
  • 1 logging interceptor class: LoggingInterceptor
  • 1 caching interceptor class: CachingInterceptor

I think this is much better. No repetition with logging and caching decorators. I can still easily wire up the dependencies with a good IoC tool. If my logging or caching needs to change, I only need to change one place. If I actually need different logging or caching implementations, I can still do that. The only major difference is that now I have to use an AOP tool like DynamicProxy or PostSharp.

So, when should you use the decorator/proxy patterns and when should you use an AOP tool? With 2 service classes, you can definitely get by with your own decorators. With 100 service classes...maybe not so much. "Somewhere in between" is my weaselly answer.

  • If you know your app will have a ton of service classes, you may as well start with an AOP tool.
  • If you know your app will only have a small amount, you may as well stick to decorator/proxy.
  • If you aren't sure, start with decorator/proxy and move to an AOP tool when you feel like there's too much boilerplate and repetition.

If you plan your architecture carefully, you can switch back and forth between tool and tool-less with a minimum of effort.

Comments

Matthew D. Groves

About the Author

Matthew D. Groves lives in Central Ohio. He works remotely, loves to code, and is a Microsoft MVP.

Latest Comments

Twitter