Section 19.6. MAP TO ASPECTJ


19.6. MAP TO ASPECTJ

As a demonstration of the independence of Theme/UML as a design model from any particular implementation model, this section looks at the mapping of crosscutting theme concepts to AspectJ.

At the conceptual level, crosscutting theme design and aspect-oriented programming in AspectJ have the same goals. Theme/UML provides a means for separating and designing reusable crosscutting behavior, and AspectJ provides a means for separating and implementing reusable crosscutting behavior. This section describes a simple approach for mapping crosscutting theme constructs to AspectJ constructs. Other approaches are possible and have been described elsewhere [5]; we discuss these briefly in Section 19.7.

19.6.1. AspectJ Programming Elements

AspectJ is an aspect-oriented extension to Java that supports the implementation of crosscutting code (i.e., aspects) as separate aspect modules. As described in [14], AspectJ adds four kinds of program elements to Java. These are aspects, pointcuts, advice, and introductions. Table 19-3 describes these elements and maps them to the corresponding design elements in Theme/UML.

Table 19-3. Mapping AspectJ Program Elements to Theme/UML

Element

AspectJ

Theme/UML

Aspect

An aspect is a crosscutting type, with a well-defined interface, which may be instantiated and reasoned about at compile time. Keyword: aspect.

A crosscutting theme is a design equivalent to an aspect.

Pointcut

During the execution of a program, and as part of that execution's scope, there are points where crosscutting behavior is required. These points are join points. A pointcut is a crosscutting set of join points. Keyword: pointcut.

Operation template parameters may be defined and referenced within interaction specifications, denoting that they are join points for crosscutting behavior. These templates may be replaced by actual operations multiple times and are therefore equivalent to pointcuts.

Advice

A piece of advice is code that executes at a pointcut, using some of the execution scope. Keywords: before, after, around.

Within an interaction diagram, crosscutting behavior may be specified on template operation calls. This behavior is equivalent to advice code.

Introduction

An introduction is a programming element, such as an attribute, constructor, or method, which is added to a type that may add to or extend that type's structure. No keywords.

Design elements that are not template elements may be defined within crosscutting themes. These may be classes, attributes, operations, or relationships and are equivalent to an introduction.


19.6.2. Algorithm for Mapping to AspectJ

The question of how to map crosscutting themes to AspectJ depends on how faithfully one wishes to represent the design-level entities. There are two chief approaches: (1) represent both a crosscutting theme and its composition specification as a single aspect, or (2) maintain the separation of a reusable crosscutting theme from its composition specification.

The first approach results in aspects that lack reusability and evolvability. It requires that the crosscutting theme be reimplemented for every composition specification that is used; each of these would need to be modified separately should the crosscutting theme need to evolve. The second approach promises a better solution and forms the basis for our analysis in this section.

Difficulties with evolving mapped crosscutting themes would be reduced if we could produce an implementation-level construct that represented a crosscutting theme alone without its composition specification. Then any changes to this crosscutting theme would affect only this one construct. This construct would be more reusable since it would not be specific to a single composition specification.

Abstract aspects potentially provide such a means of separating the code for crosscutting behavior in a reusable way. We therefore provide a direct mapping from uncomposed crosscutting themes to abstract aspects in Section 19.6.2.1. The binding of a crosscutting theme with a base theme is then mapped to concrete aspects that extend these abstract aspects; this is described in Section 19.6.2.2.

19.6.2.1 Uncomposed Crosscutting Themes Map to Abstract Aspects

The following illustrates an algorithm to be applied when mapping a reusable crosscutting theme to AspectJ. We describe the reasoning behind the major features of this mapping next.

Figure 19-13. Algorithm for mapping uncomposed crosscutting themes to AspectJ.


Pattern classes become interfaces within the abstract aspect. A pattern class (e.g., Subject) is a "placeholder" for a concrete class. In AspectJ, the only constructs provided that can act as such a placeholder are an interface or a class.

If we use a class, an inheritance relationship will need to be defined for the concrete replacing class. This is because a pattern class may name operations without providing any constraints upon their behavior, and therefore they need to map to an abstract type in AspectJ. However, a concrete class (e.g., BookCopy) that will eventually have to become a subtype of this abstract type may already possess a superclass. It would be necessary to replace any previously defined superclass of the concrete class if we chose to map the pattern class to an abstract class.

On the other hand, mapping a pattern class to an interface allows a concrete class to have added to it a declaration that it implements this interface (e.g., BookCopy implements SubjectI).

Non-template operations become introduced methods on those interfaces. An interface maps from a pattern class, so that interface must somehow represent the operations required of that pattern class. Since AspectJ permits method introduction on an interface to propagate to all classes implementing that interface, this is a convenient means to add a method to a concrete class whose name is not known prior to binding. For example, Subject::notify() becomes SubjectI.notify(), which causes any concrete classes implementing SubjectI to implicitly have the notify() method introduced on them.

Template operations without supplementary behavior become methods on those interfaces. Once again, an interface mapping from a pattern class must somehow represent the operations required of that pattern class. With a template operation without supplementary behavior, either explicitly placing the corresponding method within the interface declaration or introducing an abstract method onto the interface would suffice. We have taken the former route for the sake of clarity.

Template operations with supplementary behavior become abstract pointcuts plus advice. Advice is an obvious construct to use for designating supplementary behavior to occur on execution of the concrete method bound to a template operation. Advice permits behavior to be specified even on abstract pointcuts. Since the composition pattern is only supplementing the behavior of this template operation, it need only have an abstract pointcut to advise. Thus, Subject::aStateChange(..) becomes aStateChange(SubjectI) with after advice.

19.6.2.2 Composition Specifications Map to Concrete Aspects

Now that we have a construct representing uncomposed crosscutting themes, we need to extend it to represent a particular composition specification. The algorithm for performing this deals with many small details that are conceptually straightforward; thus, we do not give a detailed algorithm but instead touch on the highlights of the process.

The elements associated with a bind[] specification on a composition relationship are mapped to the appropriate AspectJ constructs. The concrete aspect that results from the composition specification between the Observer composition pattern and the base Library design is shown in Figure 19-6. We describe the reasoning behind the major features of this mapping next.

Concrete classes bound to pattern classes require parents declarations in the concrete aspect. A pattern class is represented through an interface declared on the abstract aspect. The act of binding a concrete class in the design model to that pattern class causes that concrete class to effectively become a subtype of the pattern class within the context of the composition pattern. Therefore, a concrete class in the implementation must be effectively a subtype of the interface corresponding to the pattern class in order to be "bound" in an analogous manner. To this end, we use AspectJ's ability to add supertypes to a class. For example, Subject is represented as the interface SubjectI; since Subject is bound to BookCopy, BookCopy must implement SubjectI.

Concrete operations bound to template operations without supplementary behavior require introduction of delegating methods on concrete classes. Each interface (e.g., ObserverI) within the abstract aspect, which corresponds to a pattern class, had declared within it a method to represent each template operation without supplementary behavior (e.g., update()). Since some concrete class has been declared to implement this interface (i.e., BookManager), it must implement each of these methods. The binding of a concrete operation to a template operation is straightforward, save for one point: their signatures can differ. So, even though the concrete class already possesses a method corresponding to that being bound to the template operation (i.e., updateStatus()), our concrete aspect must introduce an additional method (i.e., update()). This method must possess the signature expected by the aspect and must delegate to the method already possessed by the concrete class.

Concrete operations bound to template operations with supplementary behavior require concrete pointcuts. Finally, the abstract aspect declared abstract pointcuts that must be made concrete. Each abstract pointcut (e.g., aStateChange(SubjectI)) corresponds to a template operation (i.e., Subject::aStateChange(..)) in the design model. For the concrete operations bound to this template operation, the concrete pointcut must capture the execution join points of the concrete operations while exposing the target object and arguments at each join point.

19.6.3. Observer in AspectJ

The Observer crosscutting theme (refer to Figure 19-10) with its composition specification to the Library theme (refer to Figure 19-11) provides the information required for the structure of an aspect program.

We begin by creating an abstract aspect Observer to correspond to the unbound Observer crosscutting theme. The Observer crosscutting theme has two pattern classes defined; we therefore provide an interface corresponding to each within the abstract aspect. The Observer pattern class declares a template operation without supplementary behavior, update(); a corresponding abstract method is declared within the ObserverI interface on the abstract aspect.

The Subject pattern class declares a non-template association to the Vector class and a non-template operation called notify(). Each of these is introduced to the SubjectI interface, which causes them to be introduced to any classes that implement SubjectI. (This will be useful when the composition pattern is bound to the base Library theme.)

A template operation with supplementary behavior, namely _aState- Change(..), is declared on the Subject pattern class. An abstract pointcut that exposes the instance of SubjectI undergoing the state change is therefore declared within the abstract aspect. This pointcut is advised so that the notify() operation is called on the instance of SubjectI undergoing the state change. Similarly, two template operations with supplementary behavior are defined on the Observer pattern class, so two abstract pointcuts plus advice are declared for these.

 public abstract aspect Observer {   // ---- type declarations   interface SubjectI {}   interface ObserverI {     public void update();   }   // ---- introductions ----   Vector SubjectI.observers;   Vector ObserverI.subjects;   private void SubjectI.notify() {     // Post: all observers in SubjectI.observers     // are sent update() event   }   // ---- Pointcuts ----   abstract pointcut aStateChange(SubjectI s);   abstract pointcut start(ObserverI o, SubjectI s);   abstract pointcut stop(ObserverI o, SubjectI s);   // ---- Advice ----   after(SubjectI s): aStateChange(s) {     s.notify();   }   before(ObserverI o, SubjectI s): start(o, s) {     s.observers.add(o);   }   after(ObserverI o, SubjectI s): stop(o, s) {     s.observers.remove(o);   } } 

To implement the concrete binding of the Observer composition pattern to the base Library theme, we create a concrete aspect that extends the abstract aspect. Next, the composition relationship's binding specification indicates that BookCopy replaces the Subject pattern class while BookManager replaces the Observer pattern class. Therefore, within the concrete aspect, BookCopy is declared to implement SubjectI, while BookManager is declared to implement ObserverI.

The composition relationship between Observer and Library indicates that all BookCopy operations failing to meet the isQuery meta-property (namely, borrow() and return()) replace aStateChange(), while the updateStatus(BookCopy), addView(BookCopy), and removeView (BookCopy) operations from BookManager replace update(), start(.., Subject, ..), and stop(.., Subject, ..), respectively. To bind both the borrow() and return() methods of BookCopy to the _aStateChange() template operation, the abstract pointcut aStateChange(SubjectI) is implemented to capture all execution join points of the borrow() and returnIt() methods. To bind the updateStatus(BookCopy) method of BookManager to the update() template operation of the crosscutting theme, we must introduce an update() method to BookManager that delegates to updateStatus(BookCopy). And finally, to bind addView(BookCopy) and removeView(BookCopy) to _start(.., Subject, ..) and _stop(.., Subject, ..), respectively, we must implement concrete pointcuts to capture all the execution join points for those two methods.

 aspect LibraryObserver extends Observer {   // ---- Declarations ----   declare parents: BookCopy implements SubjectI;   declare parents: BookManager implements ObserverI;   // ---- Introductions ----   public void BookManager.update() {     // for each BookCopy bc in subjects     // updateStatus(bc);   }   // ---- Pointcuts ----   pointcut aStateChange(SubjectI copy):     target(copy) && args(..) &&     (execution(* BookCopy.borrow(..)) ||      execution(* BookCopy.returnIt(..)));   pointcut start(ObserverI obs, SubjectI subj):     target(obs) && args(subj) &&     execution(* BookManager.addView(BookCopy));   pointcut stop(ObserverI obs, SubjectI subj):     target(obs) && args(subj) &&     execution(* BookManager.removeView(BookCopy)); } 

Note that the operation template parameter without supplementary behavior, update(), cannot be treated as a pointcut because pointcuts can only be advised; they cannot be made to occur. The interaction diagram indicates that update() must be called within the crosscutting behavior. Therefore, references to update() are explicitly replaced with references to the operation bound to it.



Aspect-Oriented Software Development
Aspect-Oriented Software Development with Use Cases
ISBN: 0321268881
EAN: 2147483647
Year: 2003
Pages: 307

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net