In my
conversation with Andres Hejlsberg about fears, insecurity, and doubts about Aspect-Oriented Programming, I mentioned general confusion and misunderstanding that AOP and DI are competing concepts. In this article I will try to explain to you about all the differences and similarities in these two approaches. Recently, I had a great time reading Dino Esposito’s article “
Aspect-oriented programming, interception and unity 2.0, ” in the December issue of MSDN Magazine. This is a great article and I strongly advise any developer involved in .NET development to read it completely. Like many DI frameworks, and some basic frameworks (WCF, ASP.NET MVC), Unity offers a similar feature to AOP: interceptors.

What is Dependency Injection?
Dependency Injection is an architectural template that is designed to reduce dependencies between components. One of the benefits of reducing dependencies (moving from a closely-connected architecture to a weakly-connected architecture) is reducing the awareness of the components about each other. Thereby, a better separation of tasks is achieved using the design principle “a program based on an interface, and not on classes” (orig .: “program to an interface, not to an implementation”). A common misinterpretation of this design pattern among C # and Java developers consists that clients usually work with interfaces (what you declare as the interface keyword), and not with classes. I prefer to interpret it like this: “program-for-contract, not program-for-implementation” (orig .: “program to a contract, not to an implementation”). The contract for the use of a class in this case consists of public (public) members of the class, supplied with documentation (plus their pre-, postconditions, if applicable). To be even more precise: “a program based on documentation, not on classes”. Thus, if your goal is just to divide concepts, you really do not need interfaces so much. All you need are the keywords public, Internal, protected and private. It is important to note that in object-oriented design, interfaces are not dogma at all. Not all framework developers adhere to this philosophy: “The Java class library is based on interfaces more than .NET, simply because .NET developers have a negative experience using COM interfaces”
Programming based on interfaces (not classes) is useful when your component needs to work with many interface implementations. There are countless cases in which this will be very useful (after all, we all want our architecture to be extensible), but let's face it: in many cases where our component must interact with another component, we know it perfectly subsequent implementation, and we know perfectly well that no other implementation is foreseen. So why then use it here? There are two options when you may want to use the concept of a program based on interfaces:
- You depend on a component that was not provided to you, but you have a need to start development, you cannot wait. And more importantly, you already need to test your own code. You need to mock (mock) the operation of this component until it is provided to you, only for testing purposes.
- You depend on a component that has a constant state (for example, a database), some kind of real-world process (for example, a bank card processing system), or simply a dependency on a slow or unreliable resource (web service). You need to simulate the operation of these components with your own implementation for testing purposes only.
In these situations (which actually happen only when it comes to testing) it is actually very good that the components are not related in the source code, even if we know how they will depend on each other during the final installation. So how do we tie these components together? This is where Dependency Injection comes into play. With the Dependency Injection design pattern, you instruct the “container” as objects must be linked to each other, and you force this “container” to link objects to each other. In fact, you do not create class objects using constructors; you do this using “container” methods. The container decides which implementation you need, creates an object and returns it to you.
Aspect-oriented programming
Dependency Injection solves the problem of associating weakly related components with each other. Aspect-oriented programming solves a variety of problems. Indeed, in reality, where various functions and capabilities (such as auditing, logging, transaction processing) affect almost all business requirements, AOP expresses this complex reality as a compact solution, with minimal code duplication. When we want to conduct a code review, audit our code, our natural desire will be to see only one file. However, when we do a revision of the code that relates to some management of customer requirements, we do not want to see this code. As you understand, this is not possible to achieve using only object-oriented programming, and this was the reason for the emergence and development of Aspect-Oriented programming.
Similarities and differences between AOP and DI
There are some similarities between DI and AOP:
- Both achieve weak connectivity in architecture.
- Both achieve a better separation of functionality.
- Both offload some of the problems with the main code.
However, DI and AOP differ significantly in situations where they prove to be helpful:
- DI is good when you have a dependency on components, and you don’t care how they are implemented;
- AOP is good when you need to apply some behavior to a large number of code elements. Moreover, the target code does not necessarily depend on this behavior.
Dynamic proxies
So after all, how did Dependency Injection become associated with Aspect-Oriented Programming? Yes, a simple DI design pattern makes it easy to add new behaviors to components. Here's how to do it: when you request a dependency on a container, you expect to get an interface implementation. You can only guess what kind of implementation you will get, but often you play a game with it. If you ask the container to add some behavior (for example, call tracing) to the component, you will not get the object itself. Instead, you will get a dynamic proxy object. The proxy object will be between you and the requested component, and will implement the behavior you requested. And since DI frameworks have the ability to embed behavior between a client and a component implementation, DI is understood by many as one of the technologies that can enable you to develop in AOP.
Proxy-based AOP: what's good?
What I love about the proxy-based approach is how AOP fits into the concept of DI. DI helps you customize the container, how it will build components, using dependencies between objects, setting their properties and invoking initialization methods. You can configure aspects simply as some dependencies in the XML file or using C # code, so the construction and assembly of components will be unified.
The ability to add aspects, just changing configuration files, can be a very useful feature. Suppose there is some problem somewhere. And you want to track all calls to a component. You can do this without recompiling the entire application. This can be a decisive advantage in a runtime environment where there are no appropriate debugging tools. Where in order to find a problem and solve it, it may take several months.
Proxy-based AOP: What's Bad?
Proxy-based AOP, which is implemented using DI frameworks has a large number of restrictions:
They
- work only on components implemented through the interface. They do not work on all methods that are internal to the component, be it public or private, static or instance. Remember that a component is part of an application, and usually includes many classes;
- They will work only in cases where the client code receives a link to the component from the container. Aspects will not be applied if the component calls its own methods, or the component will be called without direct interaction through the container;
- Because aspects are applied at runtime, there is no way to do checks at compile time. Build-time frameworks, such as PostSharp, will provide you with errors not properly using aspects during compilation and may even provide the aspect developer with various ways to do this correctly (for example, you cannot add a caching aspect to methods that return to Stream, or to methods that have security aspects);
- It is difficult to understand (a) what aspects were applied to the parts of the code you are working with, and (b) to which parts of the code you are working with, the aspects were applied. Full frameworks, such as PostSharp or AspectJ, can highlight you full details, right in the IDE. Which helps to significantly improve the understanding of the source code.
- Run-time AOP has much worse performance than build-time frameworks. This may be of secondary importance if you apply aspects to a small number of “rough” -projected classes. However, if you will create dynamic proxies for hundreds of classes, you will feel significant costs for performance;
- Dynamic proxies were always just a third-party feature of DI frameworks and never implemented full vision, even if it is technically feasible. They are always limited to implementation in methods, and this cannot be compared with what PostSharp and AspectJ can offer.
Proxy-based AOP: What is Ugly?
When a developer begins to practice something all the time, without being distracted by competing technologies, it becomes a risk, because a person who practices something constantly feels safe. And practice becomes dogma. This happens in TDD, which imposes people to write tests at a meaningless level of detail. And the same with Dependency Injection, where people are forced to isolate all classes from each other. Remember, DI was originally designed to isolate components, not classes. Components are parts of an application, its blocks, not objects. I’m concerned that we are forced (under the pressure of society) to write much more complex applications just to meet the testing requirements, which in fact do not lead to an improvement in the quality of the final product. Remember that unit testing is a tool, not a goal. The ultimate goal is the quality of your application throughout its life.
Conclusion
Aspect-oriented programming and Dependency Injection are very different concepts, but there are a limited number of cases when they are well suited to each other. In these situations, the use of AOP within DI makes sense. For other options, you have only two choices: deactivate the dynamic proxy (and this is not the solution), or use a specialized tool for AOP development. And this dilemma is not just for me, PostSharp, or .NET. The same dilemma hangs over Java programmers who think what to choose: Spring AOP or AspectJ. AOP and DI are not competing technologies. You can use them together in one project. However, it is important to know and understand what each technology was made for and focus on the quality of your code.
')
References: