19.6. MAP TO ASPECTJAs 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 ElementsAspectJ 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.
19.6.2. Algorithm for Mapping to AspectJThe 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 AspectsThe 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.
19.6.2.2 Composition Specifications Map to Concrete AspectsNow 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.
19.6.3. Observer in AspectJThe 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. |