CSC/ECE 517 Fall 2013/oss fmv
Aspect-Oriented Programming (AOP) in Ruby on Rails is a programming paradigm which aims at preserving the modularity of Object–Oriented Programs by separating behaviors that make overall implementation scattered, bulky and tangled. It so happens with huge applications that the object boundaries in the problem domain get obscured with the introduction of more and more aspects as an application gets bigger leading to spaghetti code scenario. AOP tackles this issue and compliments the object-oriented programming principles such as DRY by removing excess duplication throughout the program by separating cross-cutting concerns, resulting in a highly modular, easy to maintain and testable application code. By isolating behavior, tight coupling between functionalities handling different logic, can be prevented. Also by separating these independent aspects such as logging, transactions and security further reuse can be made in other applications as well.
Overview
Aspect-Oriented Programming (AOP) complements OO programming by allowing the developer to dynamically modify the static OO model to create a system that can grow to meet new requirements. Just as objects in the real world can change their states during their lifecycles, an application can adopt new characteristics as it develops.<ref>*http://www.onjava.com/pub/a/onjava/2004/01/14/aop.html</ref>
Consider an example: many of you have developed simple web applications that use servlets as the entry point, where a servlet accepts the values of a HTML form, binds them to an object, passes them into the application to be processed, and then returns a response to the user. The first cut of the servlet may be very simple, with only the minimum amount of code required to fulfill the use case being modeled. The code, however, often inflates to three to four times its original size by the time secondary requirements such as exception handling, security, and logging have been implemented. The term used here is "secondary requirements" because a servlet should not need to know about the logging or security mechanisms being used; its primary function is to accept input and process it.
Aspect-Oriented Programming allows us to dynamically modify our static model to include the code required to fulfill the secondary requirements without having to modify the original static model (in fact, we don't even need to have the original code). Better still, we can often keep this additional code in a single location rather than having to scatter it across the existing model, as we would have to if we were using Object-Oriented Concept on its own.
History
AOP has several direct antecedents A1 and A2:<ref>"Aspect-Oriented Programming" "Kiczales, G.; Lamping, J; Mehdhekar, A; Maeda, C; Lopes, C. V.; Loingtier, J; Irwin, J. Proceedings of the European Conference on Object-Oriented Programming (ECOOP), Springer-Verlag LNCS 1241. June 1997."</ref> reflection and Metaobject protocols, subject-oriented programming, Composition Filters and Adaptive Programming.<ref>"Adaptive Object Oriented Programming: The Demeter Approach with Propagation Patterns" Karl Liebherr 1996 ISBN 0-534-94602-X presents a well-worked version of essentially the same thing (Lieberherr subsequently recognized this and reframed his approach).</ref>
Gregor Kiczales and colleagues at Xerox PARC developed the explicit concept of AOP, and followed this with the AspectJ AOP extension to Java. IBM's research team pursued a tool approach over a language design approach and in 2001 proposed Hyper/J and the Concern Manipulation Environment, which have not seen wide usage. EmacsLisp changelog added AOP related code in version 19.28. The examples in this article use AspectJ as it is the most widely known AOP language.
The Microsoft Transaction Server is considered to be the first major application of AOP followed by Enterprise JavaBean.
Terminology
Cross Cutting Concerns
Many applications share common functionality in their design and implementation. This shared and common logic spans across many layers and leads to repetition and bloating of the application and thereby termed as crosscutting concerns. The aim of AOP is to centralize these functionalities into modular and independent logical aspects which can be inserted into the application either during compilation or at run time, as per requirement, thereby complimenting the DRY principle. The benefits of adopting such an approach are many. Apart from keeping the application with distinct object boundaries, it modularizes aspects in separate locations which are easy to update and maintain in future as well as reuse in other web applications. Some such aspects known commonly which can be treated in AOP way are:
Authentication authorization caching communication logging validation
Join point
These are the points in the execution code where external aspects can be applied either before, after or around the span of respective method.
Advice
Advice is the code applied at the joint points designed to run automatically . These instructions can be be run before, after or around the join points in the pointcut. In our example ahead, we use the logging aspect which applies the appropriate advise whenever any method is executed. In the figure above, Advisor 1 for example applies the advice logic only before the method at join point a, whereas it applies appropriate advice to another method before join point c and after join point d.
Point-cut
The points in the application where cross-cutting concerns/ aspects need to be applied. Thus it comprises a set of join points, which in our example are the set of method invocations.
Aspect
The modularization of a behavior/concern such as logging as described in the example later is termed an aspect. In the example later, we add a logging aspect to our application by defining the joint points where advice has to be applied. The most critical point is that the Aspect represents injection points which implement a single behavior or feature. Aspects are often written to be highly reusable such that they can be used in other web applications requiring similar functionality/behavior. In the following figure, Advisor 1..n represent n different aspects each providing distinct functionality.
AOP Interaction with Ruby on Rails
Logging for tracking progress, persistence of database, transaction details etc are not inherent characteristics of the model or controller but independent of the domain logic represented by the model. The MVC framework can thus be seen to be interacting with these independent modular aspects rather than including them within their functionality as shown in figure below.
AOP'istic flavored Rails Framework
Rails framework allows methods to be called before and after a method invocation by employing method aliasing and metaprogramming features of Ruby, but using a third party library such as Aquarium, to glue the aspects is more cleaner and robust to failures ( such as plugins overriding method_missing stepping over each other). Rails already provides for filters which can be called before, after or around to wrap the controllers such as shown here:
exampleController before_filter :admin_login_required
We also show how aspect oriented programming can be employed in Rails framework by separating aspects which cross-cut the application. In the following example we illustrate it for Logging aspect. We start by building an application which stores items to be purchased in a cart. To help log user's activity while successfully adding or removing the items, logging needs to be performed for each action. As application gets bigger more aspects need to corporated such as authentication, security etc which makes it difficult to maintain and understand the code. Also it is needed for any update to any aspect be applied everywhere in the business logic where that updated aspect was injected.
class ItemsUseCase attr_reader :cart, :logger, :items def initialize(cart = cart.new, logger = Logger.new) @cart = cart @logger = logger @items = [] end
# Logging Aspect for adding items to cart def user_adds(item) items << item cart.add(item, success: self.method(:user_added), failure: self.method(:fails_to_add)) end def user_added(item) logger.info "Item successfully added: #{item.name})" end def fails_to_add(item) logger.error "Failed to add item}" end end
Introducing AOP concept for separating Logging aspect.
By separating the logging aspect to ItemsUseCaseGlue, we see that our ItemsUseCase become much simpler and having comprehensible object boundaries. It handles all the above stated issues that comes with integrating aspect knowledge in the business logic.
class ItemsUseCase attr_reader :items def initialize @items = [] end def user_adds(item) items << item end def user_added(item); end def fails_to_add(item); end end
ItemsUseCaseGlue, where we employ Aquarium for employing AOP concepts, method invocation using- calls_to is used to specify our Joint points. When invoked in the program, advice is injected to take effect before, after or around the joint point.
require 'aquarium' class ItemsUseCaseGlue attr_reader :usecase, :cart, :logger include Aquarium::Aspects def initialize(usecase, cart, logger) @usecase = usecase @ cart = cart @logger = logger end def inject! Aspect.new(:after, object: usecase, calls_to: :user_adds) do |jp, obj, item| cart.push(item, success: usecase.method(:user_added), failure: usecase.method(:fails_to_add)) end Aspect.new(:after, object: usecase, calls_to: :user_added) do |jp, obj, item| logger.info("Successfully added: #{item.name})") end Aspect.new(:after, object: usecase, calls_to: :fails_to_add) do |jp, obj, item| logger.error "Failed to add item" end end end
Finally, the application retains its pure domain object properties without including any aspect layer specifications.
class Application def initialize @items = ItemsUseCase.new @cart = Cart.new @logger = Logger.new @items_glue = ItemsUseCaseGlue.new(items,@cart, @logger) @items_glue.inject! # logic end end
AspectJ/AspectR/Aquarium
AspectJ
- AspectJ is a seamless AOP extention to java programming. Java being the base language of AspectJ, it makes it easy to learn. AspectJ has a built-in support for weaving aspects into java code using a compiler ajc, which works along with javac compiler to produce bytecode
- ajc compiler is available both as command line compiler and integrated with IDE
- Besides compile time weaving it also supports load time weaving, which weaves the aspects into the application when java loader is loading the application into JVM
AspectR
- AspectR for ruby is equivalant of AspectJ and is still in its developmental stages. It has almost all the features of aspectJ with few exceptions like around advice, pointcut designator primitives which uses boolean operators to specify join points finely.
- It supports dynamic change of advice unlike AspectJ.
- AspectR works by simply aliasing the existing methods and creating new version of them which calls advices appropriately.
Aquarium
Aquarium is a toolkit for Aspect-Oriented Programming (AOP) whose goals include
- A powerful pointcut language for specifying where to apply aspects, comparable to the pointcut language in AspectJ for Java.
- Management of concurrent aspects (i.e., those acting on the same join points).
- Adding and removing aspects dynamically.
- A user-friendly DSL.
- Support for advising Java types through JRuby.
Problem with Aspect Oriented Programming
Quality and Risks
Having looked at Aspect-Oriented Software Development from a quality viewpoint and and from some recent trends with AspectJ/AspectR , we have seen potential risks along with the benefits. We will discuss three issues that illustrate some of the challenges we may face with respect to quality as Aspect-Oriented Software Development becomes more popular.
- The first issue is how we will need to modify our process to accommodate AOP. One of the most effective techniques for detecting software defects is through code inspections and reviews. During a review, a team of programmers critiques code to determine if it meets its requirements. With Object-Oriented programs, we can review classes, or sets of related classes, and reason about them. We can look at the code and determine if it handles unexpected events properly, has logical flaws, and so on. In an Object-Oriented system, each class completely encapsulates the data and behavior of a specific concept.<ref>*http://www.ibm.com/developerworks/rational/library/2782.html</ref>
With AOP, however, we can no longer reason about a class just by looking at the code for it. We do not know whether the code might be either augmented by advice from some aspect or completely replaced by such advice. To be able to reason about an application's code, we must be able to look at the code from each class as well as the code for any aspects that might affect the class's behavior. However, it is possible that the aspects have not been written yet. If that is so, then how much can we truly understand about the behavior of the application class's code when we consider it in isolation? In fact, the way to think about correctness in AOP code is the inverse of how we consider it for object-oriented programming (OOP) code. With OOP, we go from inside out: We consider a class, make assumptions about its context, and then reason about its correctness, both in isolation, and in terms of how it will interact with other classes. With AOP, we need to look from the outside in, and determine the effects of each aspect on every possible join point. Determining how to reason correctly about AOP, and developing the appropriate techniques and tools to help us, is a rich research area.
- A second, more practical issue regarding the potential widespread adoption of AOP is the development of tools and techniques for testing, especially unit testing. Because code may be changed by an aspect, unit tests that run perfectly on a class may behave quite differently when the class is integrated into an AOP system. The following example illustrates this point.
As we probably know, a Stack is a data structure that is used to add and remove items in a last-in, first-out manner. If you push the numbers 2, 4, and 6 onto a stack and then pop the stack twice, you will get 6 and 4, in that order. Writing a unit test for a Stack class is straightforward. We can read the requirements specification and the code and do a very good job of ensuring that the implementation is correct. One of the first things a programmers will do when he begin to look at AspectJ is to implement a stack and see what he can do with aspects to change the behavior. He may implement a simple change: Increment each item by 1. This is very easy to do. Now, his unit test -- which pushes 2, 4, and 6 onto the stack and pops the top two elements off to verify that they are 6 and 4 -- fails. The code that the unit test is testing did not change, yet the behavior changed. Instead of 6 and 4, the results are now 7 and 5. This is a trivial example, and one that probably would not occur in a real-life situation. But it shows that a malicious programmer can cause a lot of harm quite easily. Even if we ignore malicious programmers, many defects occur because there are unwanted side effects of changes we make. It might be very difficult to ensure that an aspect implemented for very valid reasons does not have unwanted effects on existing program functionality.
- The third issue is the testing process itself. Once we have a set of tools and techniques, how do we modify our testing process to use these effectively and support our overall development goals? Although this issue may not be a major one, it will need to be addressed before we can really do a good job of testing software built with aspects.
Summary
Quality issues may be the biggest deterrents to adopting Aspect-Oriented Software Development methods, but they are not the only ones. Aspect-Oriented Development is a new paradigm. As did other paradigms when they were new (e.g., Object-oriented software development), this one will take time to be adopted widely because it involves a learning curve. First we must learn basic techniques and mechanics, then advanced techniques, and then how to best apply the techniques and when they are most appropriate. Tools will play a major part in industry adoption of Aspect-Oriented Programming. In addition to compilers and editors, we need tools to help us reason about systems, identify potential cross-cutting concerns, and help us test in the presence of aspects. As we find methods to help us represent the systems better, such as representing aspects in UML, our tools must evolve to support these methods. Although we discussed AspectJ, AspectR, Aquarium very succinctly in this article, they are not the only AOP implementations.
Furthermore, other paradigms similar to AOP are also emerging. The multi-dimensional separation of concerns paradigm, for example, has been under development at IBM Research, and an implementation is available in the HyperJ tool. Using any new paradigm is risky until standard implementations are established for the languages you work with.
AspectJ, for instance, is a still-evolving AOP implementation. The danger is that you may develop software that incorporates cross-cutting concerns, and your implementation approach will either cease to be supported or will change significantly.
Moving toward a better way to build software
Clearly, we have a way to go in developing tools and processes to make AOP viable for everyday use. However, there is no reason to believe that any of the issues we have discussed in this article represent insurmountable problems. We do believe that when we have developed a proven set of tools and processes for AOP, we will have a better way to build software than we do today. As Barry Boehm said about agile processes we must approach AOP with care.<ref> Get Ready for Agile Methods, with Care", Barry Boehm, IEEE Computer, January, 2002 </ref> Whether we are early adopters or we wait for this technology to become more mainstream, we need to ensure that our software investment will provide acceptable returns today and in the future. That's just good business sense.
References
<references/>
Further Reading
- Google-guice - a light weight dependency injection framework by java
- An Initial assessment of aspect-oriented Programming, Robert J Walker, Elisa L.A
- Adaptive Object Oriented Programming: The Demeter Approach with Propagtion Patterns Karl Liebherr 1996 ISBN 0-534-94602-X
External Links
- Aspect-Oriented Programming on Wikipedia
- http://java9s.com/spring-framework/spring-aop-tutorial-aspect-oriented-programming
- http://guides.rubyonrails.org/action_controller_overview.html
- http://www.codeproject.com/Articles/11385/Aspect-Oriented-Programming-in-NET
- https://en.wikipedia.org/wiki/Cross-cutting_concern
- http://msdn.microsoft.com/en-us/library/ee658105.aspx
- http://aquarium.rubyforge.org/examples.html
- http://www.onjava.com/pub/a/onjava/2004/01/14/aop.html
- http://www.ibm.com/developerworks/rational/library/2782.html
- http://www.onjava.com/pub/a/onjava/2004/01/14/aop.html
- http://blog.arkency.com/2013/07/ruby-and-aop-decouple-your-code-even-more/
- http://aspectr.sourceforge.net/