AOP: The 15% Solution
started 4/20/2002; 1:36:24 PM - last post 4/23/2002; 6:50:13 PM
|
|
Chris Rathman - AOP: The 15% Solution
4/20/2002; 1:36:24 PM (reads: 3469, responses: 15)
|
|
AOP: The 15% Solution |
A short interview with Gregor Kiczales of the Parc team that developed Aspect Oriented Programming and AspectJ.
A group here at PARC has been looking at ways to improve software architecture. For about 12 years, we've been thinking that crosscutting was "it." We first worked on reflection and metaobject protocols. That had almost too much power; people couldn't make effective use of it. Then, around 1992, John Lamping and I took another stab at how to capture crosscutting. Over a series of years, we developed AOP and AspectJ.
I can't say that I'm completely sold on AOP just yet. While it's true that OOP's reliance on hierarchical data structures is a weakness, I'm not sure opening that structure to crosscutting aspects (parasites?) makes reasoning about them any easier.
That said, I do see 3 primary advantages of AspectJ: (1). It's very nice for tracing, monitoring, and debugging; (2). Visitor and Observer patterns become a whole lot less cumbersome; and (3). it allows implementation methods to be defined for Interfaces (Obligatory toy code). But there's probably some other things I'm missing as well...
Posted to OOP by Chris Rathman on 4/20/02; 3:54:52 PM
|
|
|
|
Patrick Logan - Re: AOP: The 15% Solution
4/21/2002; 10:49:40 PM (reads: 2374, responses: 0)
|
|
"Aspects" is a mysterious term. I think it is captured better in languages and implementations that are not attempting to be so mysterious, and are not using such low level languages as Java.
|
|
Chris Rathman - Re: AOP: The 15% Solution
4/22/2002; 7:15:30 AM (reads: 2341, responses: 0)
|
|
While Java is far from the best language to expound the virtues of aspects, I suspect that Java's limitations present the greatest opportunities for exploitation. I agree, though, that it tends to complicate matters significantly. I guess I'd rather see how they'd work in language like Eiffel, where there are not so many discontinuities in the OO language.
|
|
Ehud Lamm - Re: AOP: The 15% Solution
4/22/2002; 7:36:16 AM (reads: 2333, responses: 0)
|
|
Chris, can you elaborate on "(3) allows implementation methods to be defined for Interfaces"?
Obviously when you define interfaces, you are allowed to create routines that take advantage of these interfaces. If, on the other hand, you are want to implement methods, how can you keep on calling these interfaces?
I guess I am being a bit thick. Sorry.
|
|
Chris Rathman - Re: AOP: The 15% Solution
4/22/2002; 8:09:20 AM (reads: 2341, responses: 1)
|
|
What I meant to say is that you can define a shared implementation for an interface. If for example, you have two classes that implement the same interface, they both are required to implement the method to satisfy the interface requirements. This can be annoying if the method is identical between implementing classes, because the interface only defines the signature, having no provision for sharing a common implementation between the various implementors.
|
|
Ehud Lamm - Re: AOP: The 15% Solution
4/22/2002; 8:40:23 AM (reads: 2399, responses: 0)
|
|
I still don't get it.
If the method can be implemented using the other methods in the interface - go ahead and implement it. You now have a shared implementation, right? If the method can not be implemented solely on the basis of the other interface methods, than how can you be sure a shared implementation is possible?
Or maybe you are thinking about a sitation in which you can not be sure in advance that sharing an implementation is going to be possible?
|
|
Chris Rathman - Re: AOP: The 15% Solution
4/22/2002; 9:09:37 AM (reads: 2312, responses: 1)
|
|
The problem arises from Java's lack of multiple inheritance for classes. Although Interfaces provide MI of a sort, each class must implement all methods. I guess as background, I'd cite a JavaWorld article:
Lack of default implementation
The final problem is the lack of default implementation. When a class extends another class, this other class can provide default implementations for all the methods. Sometimes a subclass can be created simply by extending a superclass and using the entire implementation of the superclass. Some interfaces are quite extensive, and implementing all methods can be a lot of work. This is especially irritating when only one or two methods are useful to implement in a certain situation. The interface concept forces the user of the interface to implement every method in the definition. For example, the Swing ButtonModel interface has 21 (!) methods defined. Quite often, an implementation wants to implement only a part of this interface, and it should be possible to do this.
Although AspectJ provides some relief by allowing you define a single method which cross-cuts across the classes that implement an interface, it doesn't necessarily do it in a satisfactory manner. Because the Aspect rides on top of the class definitions, the classes themselves can't override the definition. What it does allow you to do, however, is define a single implementation for all the specified classes.
|
|
Ehud Lamm - Re: AOP: The 15% Solution
4/22/2002; 9:20:43 AM (reads: 2409, responses: 0)
|
|
Although Interfaces provide MI of a sort, each class must implement all methods
Yes, of course. But my point was the the extra method can be implemented by using the other methods in the interface. (And thus it won't be part of the interface per se). Can't this be done?
Quite often, an implementation wants to implement only a part of this interface, and it should be possible to do this.
This obviously has things backwards. The point of defining an interface is to require classes that support that interface to provide all methods. Otherwise why claim you implement the interface? (Indeed, this scenario is an example of what comes under the heading of abstraction breaking in my book).
|
|
Chris Rathman - Re: AOP: The 15% Solution
4/22/2002; 10:05:11 AM (reads: 2327, responses: 0)
|
|
I suppose one can differentiate between Type Inheritance and Code Inclusion (ala Sather). In this case, we are not concerned with the inheritance of type - it is defined in Java via either class inheritance or interface signature - where the type is determined by the messages the class responds to and it's associated properties.
The question is not whether the class must implement all methods of the interface, but rather how one goes about accomplishing that feat. With true multiple inheritance (or mixins), the code that implements the method can be inherited from a base class - which provides not only the abstract data type, but also the code that serves as a base implementation for the subclasses.
In the case of Aspects, you are providing an implementation that can cut across classes - assuming that these classes have a common implementation which satisfies the methods stipulated in the interfaces. The idea being that if the same code is being duplicated in two places, there is probably some abstraction that is either being missed or, in this case, hard to satisfy because of the constraint of single inheritance for code inclusion.
I guess one could make the case that the Visitor pattern does have problems related to breaking the abstraction. The Visiting class must be familiar with the associated classes - doing various chores for the target classes (which is why I used the unfavorable term of parasite). But the bottom line for AOP is that they are claiming that there are abstractions which are not fully captured by the current slate of programming languages that are organized along class (or procedure/function) boundaries. They are trying to provide a release for capturing abstractions which cross-cut the hierarchies that arise in the current environments.
Whether they've succeeded in a satisfactory manner is, of course, debatable.
|
|
Anton van Straaten - Re: AOP: The 15% Solution
4/22/2002; 10:22:14 AM (reads: 2322, responses: 1)
|
|
Ehud wrote:
If the method can be implemented using the other methods in the interface - go ahead and implement it. You now have a shared implementation, right?
Um, wrong? In Java, the definition of an interface cannot contain any implementation. So there's no way to reuse the implementation of an interface method across multiple classes, other than by implementation inheritance (or cutting and pasting code, or manual delegation, etc.)
In Java, implementation inheritance is not the solution, since it is restricted to single inheritance, which prevents the use of interfaces to provide common functionality across classes otherwise unrelated by implementation inheritance.
The point of all this is that even if you ignore everything else about AOP, the ability to reuse a shared implementation of interface methods across classes otherwise unrelated by implementation inheritance is a big improvement for Java.
|
|
Oleg - Re: AOP: The 15% Solution
4/22/2002; 5:05:58 PM (reads: 2303, responses: 0)
|
|
I'm very concerned about AOP and its lack of safety. We're talking
about inserting code into another function or method: generally
imperative code into the midst of another imperative code. Suppose I
weave in a debugging aspect after a particular point in a
method. Unbeknownst to that, you weave in another aspect, after the
same point. How are our aspects to be ordered? In imperative code, the
order is very important. Two independent aspects may interfere via
common binding. The list of problems goes on and on. These are real
problems, acknowledged even by the proponents of AOP.
An article "Discussing aspects of AOP" by Tzilla Elrad, Mehmet Aksits,
Gregor Kiczales, Karl Lieberherr, Harold Ossher (Comm. ACM, October
2001, pp. 33-38 -- part of the special issue on AOP) says:
"A related issue is how to work with large numbers of aspects. How do
they compose? How do we think about such designs? What notations
should we use for describing them? These and other related questions
are ones we expect the user and research communities to explore over
the next few years."
[Indeed, what if a concern weaver introduces a concern (e.g., logging)
into a critical section of some other code -- which may lead to a
deadlock. Are concerns associative and commutative? Concerns are in
general imperative -- so they are not.]
"HO: One of the most important open [sic!] issues is semantic
correctness of aspects and compositions. It has always been an issue
with modular systems to ensure that modules are correct on their own
and that they interact correctly when composed. The join point-based
composition provided by aspect languages is much richer than the
interface- or message-based connection provided by most other
modularization mechanisms, complicating both specification and testing
of compositions. Even specification and unit testing of individual
aspects is an area requiring research.
The concept of correctness of a reusable aspect is an
open issue [sic!]. We need to find good ways to express the
assumptions that need to hold for a reusable aspect to
work correctly in a specific context."
The correctness of AOP is still an open issue! I find it hard to take
such an approach seriously.
|
|
Ehud Lamm - Re: AOP: The 15% Solution
4/23/2002; 1:01:11 AM (reads: 2339, responses: 0)
|
|
Thanks guys. I guess I was just too tired to think straight. I was thinking about what you can do with interfaces in general, as opposed to what you can do with interfaces in Java..
|
|
Ehud Lamm - Re: AOP: The 15% Solution
4/23/2002; 1:11:41 AM (reads: 2255, responses: 0)
|
|
Oleg,
I am not sure I agree with you on this one. Aspects are a form of abstraction breaking. When you break into someone else's abstraction you must be careful not to break something important (an invariant, a timing property, mutual exclusion, whatever).
This makes the technique problematic, but what's your solution?
When programming without a crystal ball, you must assume that future programmers will be careful when doing changes. Of course you want to minimize the chance of problems, and make maintenance as painless as possible -- but there's no way to eliminate the risk completely.
|
|
Oleg - Re: AOP: The 15% Solution
4/23/2002; 10:49:12 AM (reads: 2259, responses: 1)
|
|
It appears that before AOP, the most common way of creating a complex
software artifact was by combining or assembling smaller and simpler
pieces. This engineering principle appears in its purest in Functional
Programming. However, almost all other languages since Fortran and
assembly programming have strongly encouraged modularity. Speaking of
FORTRAN, what makes it an attractive choice for numerical scientists
even today is the large body of well-written numerical modules
(subroutines). I don't need to know how SVD is implemented in order to
use it. Therefore, the idea of the separation between the
implementation and the interface is stressed. The separation is rarely
perfect, yet programmers usually strive for a better, more complete
separation.
AOP seems to turn this principle around. To weave in a concern, I do
need to know a great deal about the implementation. I need to examine
the code to make sure I do not insert the concern in an unappropriate
place. Some AOP techniques place the burden on the original programmer
to specify suitable insertion points. A concern can be weaved in only
at these points. However, when several concerns are to be weaved in,
the problem of their ordering and interaction arises.
When programming without a crystal ball, you must assume that future
programmers will be careful when doing changes. Of course you want to
minimize the chance of problems, and make maintenance as painless as
possible -- but there's no way to eliminate the risk completely.
When I invoke the function sin(x) in any language, I can safely assume
that sin(x) will return the same result given the same value of x. I
don't need a crystal ball to make and use this assumption. I'm
greatly concerned with the mentality that is epitomized by the
function gets() and C approach to error handling in general. Surely
nobody will give gets() a string longer than the allocated
buffer. Surely a programmer will test the result of malloc(). Surely
the programmer will make sure that the result of strcat() will fit the
allocated buffer. A quick glance at SANS security advisories shows
where this approach leads to. I agree that we have to strive to
minimize risk; I'm not sure that AOP does that.
|
|
Ehud Lamm - Re: AOP: The 15% Solution
4/23/2002; 11:13:47 AM (reads: 2325, responses: 0)
|
|
Information hiding, abstraction (both procedural and data abstraction) are, of course, very important. They are the cornerstones of software engineering.
Alas, there are many cases when we find ourselves breaking abstraction boundaries. The question is how to do this while minimizing risks. I am still researching this this issue, and I am not claiming AOP is a good solution. But I think that dismissing it because some risks still remain, is simply asking for too much. No abstraction breaking mechanism can guarantee this.
|
|
Chris Rathman - Re: AOP: The 15% Solution
4/23/2002; 6:50:13 PM (reads: 2261, responses: 0)
|
|
One of the advantages I've found in studying different programming languages is that the emphasis and terminology varies between the communities. Sometimes it helps to have very similar concepts explained in very different fashions. I don't know if it's stating the obvious, but there's a couple of language projects that I'm reminded of after reading the AspectJ documentation.
The first is the TOM Programming Language. TOM is an Objective-C derivative that likes to use the term Unplanned Reusability. The emphasis is placed on being able to keep class definitions Opened for reuse - trying to bend the Open-Closed Principle on the side of Open. The idea is that as Objects are reused over time, they must be open to all sorts of modifications well beyond simple subclassing. From the TOM documents:
Extensibility of classes encompasses the ability to do, without recompiling past code:
- add methods
- replace methods
- add state (instance variables), as possibly needed by new methods
- add superclasses/supertypes.
Note, that the addition of methods and state is accomplished somewhat in Objective-C via Class Categories. Like TOM, AspectJ attempts to open up the definition of classes to unplanned reuse. The most severe limitation of AspectJ, however, is that it currently supports join points and additions for which you have source code - preventing such Objective-C notions as adding methods to the base library types like Object and Strings (NSObject & NSString).
Another language that also has these notions built in is Cecil. In fact, one of the offshoot projects of that group is MultiJava:
MultiJava is a backward-compatible extension to Java that addresses some of the extensibility limitations of traditional object-oriented languages. Open classes allow one to add to the set of methods that an existing class supports without editing that class or its clients, obviating the need for tedious workarounds like the "visitor" design pattern. Multimethods generalize traditional receiver-based dynamic dispatching by allowing all arguments to be uniformly dispatched upon, resolving an asymmetry that makes some common idioms difficult to program. MultiJava retains Java's class-based encapsulation properties as well as its modular typechecking and compilation schemes.
In Cecil, the emphasis is on the terminology of multi-methods and visitors with much the same goals as AspectJ. Indeed, languages like Goo and Dylan (not to mention CLOS) are also related in their concept of multi-method delegation where new methods and properties can be added to change the type represented by a class (with type here defined in terms of state and behavior of an object). Need to add a method to a class, just define the method with the appropriate parameters. No need to worry about the base classes being Closed. Cecil also allows one to redefine the superclass/supertype, much like TOM sought to do.
In the world of AspectJ, the terminology used is Introductions, which has the same goals as TOM and MultiJava. AspectJ also tries to go beyond that and introduce the notion of Join-Points, where calls to methods can be trapped by a Join-Point and given Advice. That advice can be used to achieve any number of goals (e.g. debugging, DbC, error trapping, etc).
Anyhow, I thought I'd throw these thoughts into the mix.
|
|
|
|