AOP grew out of the research labs of the famous Xerox Palo Alto Research Center (PARC). The paradigm resulted from work undertaken by a number of teams on several different areas of software engineering including reflection and alternative variants of object-orient programming (OOP). A team led by Gregor Kiczales identified a key shortcoming in conventional programming paradigms, the ability of these languages to contend with crosscutting concerns, and AOP was the result of research into addressing these shortcomings.
To understand how the AOP paradigm can benefit the software development process, it is necessary understand the nature of the crosscutting concern issue that AOP addresses.
Object-oriented design focuses on the clean separation of concerns within a system. A concern is a concept, objective, or functional area that is of interest to the system. For example, a core concern of a stock-control system would be tracking stock items held at the warehouse.
Object-oriented languages provide the constructs necessary to localize the different concerns of a system into discrete modules. This is a fundamental approach to good software design and was practiced long before OOP came on the scene. Procedural languages put the rudimentary building blocks in place to enable the functional decomposition of concerns into modules. OOP built on the structures offered by procedural languages for modularization, introducing concepts such as information hiding and abstraction to facilitate the separation of concerns within a software system.
The problem with concerns is that they don't all fit neatly into little boxes. Some concerns freely cross module boundaries and intersect with other concerns. These are known as crosscutting concerns and are generally associated with system-level concerns such as logging, threading, security, transactions, and performance. Such concerns are orthogonal to the core business concerns.
Chapter 4 discusses the concept of orthogonal design in software architectures.
Crosscutting concerns are a dilemma for the software engineer, as OOP languages fail to offer a convenient mechanism for protecting the code base from the unwanted complexity they introduce into even the cleanest of software designs.
Code Tangling and Scattering
Crosscutting concerns are damaging to the design of software systems. They introduce the two unwanted side effects of code tangling and code scattering into the software architecture. Code tangling and scattering adversely affect the readability and maintainability of the application:
The impacts of crosscutting concerns on enterprise software are detrimental to the objectives of producing applications that are both readable and maintainable. Software that is hard to understand is difficult to maintain; code that is difficult to maintain cannot undergo rapid change in response to the evolving requirements of the business.
Fortunately, OOP is not completely defenseless against the ravages of crosscutting concerns, and various strategies are available to keep their impact to a minimum. These strategies essentially include the use of frameworks, design patterns, domain-specific languages, and mix-in classes. We briefly cover each of these techniques before examining how AOP addresses the issue.
Traditional Approaches to Crosscutting Concerns
The next sections look at the main techniques available for managing crosscutting concerns within the confines of a single paradigm OOP language such as Java.
Frameworks offer an effective means of separating core and system-level concerns, and the J2EE platform is an excellent example of this.
The J2EE platform frees the software engineer of many of the major system-level concerns that, by their very nature, tend to fall into the category of crosscutting concerns; threading and security are just two examples of system-level concerns that J2EE makes transparent to the developer. Each of these concerns is imperative for a high-performance, secure system, yet J2EE technology enables the developer to focus on implementing business knowledge and delegates the task of managing threads and authenticating and authorizing users to the J2EE server.
Having the J2EE server take responsibility for some of the major crosscutting system-level concerns significantly reduces the level of code tangling and scattering throughout the code base. Thread synchronization code does not appear in any of the application's components because threading is entirely handled by the J2EE container, although the developer must still be wary of certain design decisions, such as the use of the Singleton pattern. Likewise, security in J2EE is effectively modularized by regulating security concerns to the deployment descriptor.
Despite the inarguable effectiveness of the J2EE platform in dealing with many system-level concerns, not all crosscutting concerns are system-level concerns. The J2EE platform cannot manage application-specific business crosscutting concerns, nor can the platform easily accommodate additional ad hoc system-level concerns not catered for by the platform. We therefore have only a partial solution to the crosscutting problem.
Fortunately, J2EE and AOP are not mutually exclusive, and AOP can build on the services of the J2EE platform.
With careful design, it is possible to devise solutions with OOP techniques that can modularize crosscutting concerns, separating them from the system's core concerns. The Visitor pattern is a good example of such a design and is applicable where objects within a hierarchy perform a number of unrelated operations [Gamma, 1995].
The authors of the Visitor pattern saw a need to prevent unrelated operations from polluting the classes within the hierarchy. Pollution of classes relates directly to the crosscutting concerns we are attempting to manage.
A Visitor pulls together the different and diverse operations performed on a particular class. The main class then accepts the Visitor, requesting it to perform these unrelated, crosscutting operations on its behalf.
Visitor gathers related operations together and separates unrelated ones, thereby promoting an application architecture that sees an object model aligned along concern boundaries.
The disadvantage of the Visitor pattern is that the main classes in the object hierarchy must be aware of Visitor objects and be prepared to invoke their behavior. Arguably, this is a crosscutting concern in itself, as framework code for the implementation of the pattern now must become part of the application. Code tangling is still evident within the code, albeit at a reduced level.
The concept of mix-in classes is more appropriate for OOP implementations that support multiple inheritance than for single-inheritance languages like Java.
With this technique, functionality resides in discrete, loosely coupled and highly cohesive classes. Each class defines a specific behavior or trait. Fully featured classes result by mixing the different classes available from the object hierarchy. This approach enables the various concerns of a system to be assembled using multiple inheritance. The mix-in approach is workable, but very careful design of the class hierarchy is required to achieve a breakdown of functionality at the correct level of granularity.
Although Java does not support multiple inheritance per se, it can simulate the technique using interfaces and composition. The mix-in class design is more cumbersome to implement using these constructs than with true multiple inheritance. However, if Java were to ever support multiple inheritance, the complexities associated with its use, such as object construction order, shared-data member instances, and the need for virtual inheritance, would all outweigh the benefits the mix-in technique offers. Moreover, even with this technique, the main class still must orchestrate the calling of the different methods.
To contend with crosscutting concerns, it is possible to look to other paradigms than OOP for helpfor example, using a rule-based language for implementing business logic.
Chapter 10 introduces rule-based languages.
Here, a rule-based language implements business concerns, while the services of the J2EE platform handle system-level concerns. Java serves as the glue between J2EE services and the rule-based language.
This is an effective approach because rule-based languages offer an excellent vocabulary for defining business logic. However, J2EE components still need to know the point at which to fire the business rules, and again, this represents a mixing of concerns.
The ideal solution for crosscutting concerns would see core concerns implemented in ignorance of orthogonal crosscutting concerns, thereby avoiding code tangling. Moreover, a language should offer the ability to modularize crosscutting concerns in the same way as is currently possible with core concerns in OOP languages. This would prevent code scattering.
The AOP paradigm provides the language constructs necessary to achieve these aims and is examined in the next sections.