CSC/ECE 517 Summer 2008/wiki1 5 a5: Difference between revisions
Line 122: | Line 122: | ||
} | } | ||
Neither Screen's nor Point's code has to be modified to make this work, and all the changes needed to support this new capability are local to this aspect. | |||
==== AOP in Ruby ==== | ==== AOP in Ruby ==== |
Revision as of 02:10, 6 June 2008
Introduction
Ruby has hooks that allows trapping a certain event (e.g., object creation) and running a particular code sequence whenever the event occurs. There's no comparable facility in Java. But both Ruby and Java have support for aspect-oriented programming (AspectR and AspectJ, respectively). What's the difference between simply providing hooks, and supporting full AOP, and why is it more convenient to program this way in Ruby than Java? Give a few code sequences to justify your conclusions.
Hooks
There is a strong desired from developers to have control on object life cycle. They would like to know when an object is created or destroyed, or some specific method is executed. They would like to monitor what application is doing inside and look for possibility to change application behavior or add new functionality without making big changes in object model. Some time programming languages provide hooks to monitor such events. Hooks are set of code that automatically execut when a particular event triggers.
Hooks Implementation
Most of dynamic languages provide some ways to execute custom code at different steps of object life cycle. Most of them built this functionality as part of language design. They use interceptor design pattern as implementation guideline.
Hooks in Ruby
Ruby is a dynamic and pure object oriented language. It has a very strong support for metaprogramming. It provides system hooks for monitoring events like object creation. The technique use by Ruby to provide this functionality is a simple example of interceptor design pattern. By intercepting calls to system classes developer could modify the system behavior without changing application code.
Example Code:
Following is an example code from Programming Ruby - The Pragmatic Programmer's Guide (First edition is freely available on web) for hooking objects creation event. This code modifies two Ruby system classes (Class and Object). It renames and redefines the Class new method and modifies the Object class to store timestamp.
class Class alias_method :old_new, new def new (*args) result = old_new(*args) result.timestamp = Time.now return result end end class Object def timestamp return @timestamp end def timestamp = (aTime) @timestamp = aTime end end
Now, lets run some tests:
class Test end obj1 = Test.new sleep 2 obj2 = Test.new obj1.timestamp obj2.timestamp
Hooks in Java
Java is statically type language. It doesn’t provide any language level ability to monitor events like object creation etc. However, the latest JVM version through JVMTI allow some hooks for monitoring performance but there is no way for the developer to change the behavior of an application through the available hooks.
Benefits of Hooks
Hooks is an elegant way of changing application behavior in dynamic languages. It is simple and compatible with dynamic languages philosophy. Since most time it is part of language, developer does not have to learn new API to take advantage of this.
Aspect-Oriented Programming
Object-Oriented Programming (OOP) was a big shift from procedural programming. Now instead of concentrating on procedures and data separately, developers start working with objects and their interactions to design applications. This allowed them to deal with large and more complicated system and develop them in less time ever before. Later Aspect-oriented Programming (AOP) made developers life easier by allowing them to dynamically modify the static OO model to create a system that can grow to meet new requirements.
Aspect-Oriented Programming helped the developers in separation of concerns, specifically cross-cutting concern, as an advance in modularization. – Wikipedia
AOP is look very simple in definition but it is quite complex topic. Before going further, it is better to familiarize ourselves with some related terminology.
- Cross-cutting concerns: Even though most classes in an OO model will perform a single, specific function, they often share common, secondary requirements with other classes. For example, we may want to add logging to classes within the data-access layer and also to classes in the UI layer whenever a thread enters or exits a method. Even though the primary functionality of each class is very different, the code needed to perform the secondary functionality is often identical.
- Advice: This is the additional code that you want to apply to your existing model. In our example, this is the logging code that we want to apply whenever the thread enters or exits a method.
- Point-cut: This is the term given to the point of execution in the application at which cross-cutting concern needs to be applied. In our example, a point-cut is reached when the thread enters a method, and another point-cut is reached when the thread exits the method.
- Aspect: The combination of the point-cut and the advice is termed an aspect. In the example below, we add a logging aspect to our application by defining a point-cut and giving the correct advice.
[taken from Introduction to Aspect-Oriented Programming]
AOP Implementation
For both Java and Ruby multiple implementations of AOP exist. For the purpose of this article we will look at AspectJ for Java and AspectR for Ruby.
AOP in Java
AspectJ adds the concept of a join point to Java as well as a few new constructs: pointcuts, advice, inter-type declarations and aspects. The dynamic parts of AspectJ include: the join point which is a well-defined point in the program flow, the pointcut which picks out certain join points and values at those points, and lastly a piece of advice which is executed when a join point is reached. AspectJ also has different kinds of inter-type declarations that allow the programmer to modify a program's static structure, namely, the members of its classes and the relationship between classes. AspectJ's aspect are the unit of modularity for crosscutting concerns. They behave somewhat like Java classes, but may also include pointcuts, advice and inter-type declarations.
Example Code:
Suppose we want to have Screen objects observe changes to Point objects, where Point is an existing class. We can implement this by writing an aspect declaring that the class Point Point has an instance field, observers, that keeps track of the Screen objects that are observing Points.
aspect PointObserving { private Vector Point.observers = new Vector(); public static void addObserver(Point p, Screen s) { p.observers.add(s); } public static void removeObserver(Point p, Screen s) { p.observers.remove(s); } pointcut changes(Point p): target(p) && call(void Point.set*(int)); after(Point p): changes(p) { Iterator iter = p.observers.iterator(); while ( iter.hasNext() ) { updateObserver(p, (Screen)iter.next()); } } static void updateObserver(Point p, Screen s) { s.display(p); } }
Neither Screen's nor Point's code has to be modified to make this work, and all the changes needed to support this new capability are local to this aspect.
AOP in Ruby
Benefits of AOP
Software engineers should take advantage of aspect-oriented programming to reduce the amount of scattered or tangled code within a program. Modular programming techniques such as AOP often allow engineers to fully seperate programming tasks this allows the code to be easier to read and maintain. Similar to the way the OSI Reference Model breaks up networking transmission into layers of related functions. When creating software or hardware for a single layer, engineers must only provide services for the layer above and below the one currently being designed. This allows Application Layer programmers to do their job with little or no concern for what is going on at the Physical Layer.
Using AOP to break down the functions of a program allow software engineers to work on one function without breaking another. While this may seem like common sense, it can be a very difficult task. For example when coding a database update function a few of the considerations include ensuring the user updating the database is authorized to make the change, the data is valid in respect for the field being updated, and that the change is properly logged so that it may be undone if needed later. These concerns along with many others can be properly be encapsulated so that they may be maintained and updated without affecting the various other processes going on along side them.
AOP vs Hooks
From earlier days of object-oriented programming languages, programmers are using different techniques to execute code at different phases of object life cycle. They have successfully used programming concepts like subjects, interception and delegation to deal with such requirements. All modern object-oriented dynamic languages now support hooking mechanism for such situations.
Aspect Oriented Programming on other hand is a new concept. It provides more comprehensive solution for dealing with complex requirements related to cross-cutting concerns and modularity. It has its own domain-specific language to deal with such problems. There is no comparison between AOP’s extensive semantics with hooks implementation. Hooks could be use as AOP implementation mechanism but they will never be able to compete with AOP functionality.
It is hard for us to come up with some good example that shows the limitation of hooks compare to AOP. Since both of us has no experience in Ruby and AOP. But while searching on web we come across a specification for implementing Cut-based AOP in Ruby. This specification discusses how we could add additional functionality to existing class without subclassing it. It introduces a new class called Cut. It is used to encapsulate advice for a single class. Following is an example for adding new functionality to existing class C without subclassing it using new Cut class,
class C def f(*args); 1; end def g(*args): 2; end end
In order to add new functionality we would subclass C. i.e.
class A<C def f print ‘(’, super, ‘)’ end end
Now with AOP extension for Ruby,
cut A < C def f print ‘(’, super, ‘)’ end end
Now instead of subclassing C “we have cut-across behavior of C with A”.
Now we will discuss how Ruby hooks are still better. If we compare above two examples (Ruby Hook and Java AOP) we could see using AOP is over kill for such simple problem. Ruby hooks are more elegant solution in such situation. AOP are more suitable for statically type languages like Java. Ruby as dynamic language needs simpler syntax for dealing with simple cross-cutting concern problems. It supports very strong metaprogramming functionalities using interceptors, hotswapping, mixins and hooks that make lot easier for injecting custom code during runtime. Different Ruby experts also share the same opinion,
“For Ruby developers, AOP is not quite as urgent, because you’ve already got robust tools to deal with these kinds of concerns.” Bruce Tate – Beyond Java.
“A standardized AOP framework has never really taken off in Ruby because the language itself already supports most of the desirable functionality of AOP.” David Heinemeier Hansson – Rail Architect