Memoirs

A reusable observer pattern implementation using package templates

Description
A reusable observer pattern implementation using package templates
Categories
Published
of 6
All materials on our website are shared by users. If you have any questions about copyright issues, please report us to resolve them. We are always happy to assist you.
Related Documents
Share
Transcript
   This is the authors' version of the work. It is posted here by permission of ACM for your personal use. Not for redistribution. The definitive version was published in the Proceedings of the 8th Workshop on Aspects, Components, and Patterns for Infrastructure Software (ACP4IS’09),   http://doi.acm.org/10.1145/1509276.1509286   A Reusable Observer Pattern Implementation UsingPackage Templates Eyvind W. Axelsen University of OsloDepartment of InformaticsPostboks 1080 Blindern, 0316Oslo, Norway eyvinda@ifi.uio.noFredrik Sørensen University of OsloDepartment of InformaticsPostboks 1080 Blindern, 0316Oslo, Norway fredrso@ifi.uio.noStein Krogdahl University of OsloDepartment of InformaticsPostboks 1080 Blindern, 0316Oslo, Norway steinkr@ifi.uio.no ABSTRACT In this paper, we show how  package templates , a new mecha-nism for code modularization, paired with a comparatively(and intentionally) small AOP mechanism may be utilizedto create a reusable package for the Observer design patternthat can be plugged into an existing architecture with a min-imum of “glue code”. Categories and Subject Descriptors D.3[ Software ]: ProgrammingLanguages; D.3.3[ Programm-ing Languages ]: LanguageConstructsandFeatures— Classesandobjects ;D.3.3[ ProgrammingLanguages ]: LanguageCon-structs and Features— Patterns General Terms Languages, Design 1. INTRODUCTION The concept of design patterns [6] is an approach to ob- ject oriented design and development that attempts to fa-cilitate the reuse of conceptual solutions for common func-tionality required by certain  patterns  or classes of commonproblems. As such, they seem like a perfect candidate forinclusion as reusable components in frameworks, infrastruc-ture software, etc. However, it seems that even though theconcepts of many patterns are in relative widespread use,implementing them as reusable components in mainstreamlanguages like C #  or Java is hard. This is at least in part dueto limitations with regards to e.g.  extensibility of the subtypingrelation  [13] and/or lack of support for mechanisms dealingwith crosscutting concerns.One commonly used design pattern is the Observer pat-tern. A few implementations utilizing various new languageextensionsalreadyexist,notablyonebyHannemannandKic-zales [8] utilizing AspectJ [2], and one by Mezini and Oster-mann [11] utilizing the Caesar system [1]. Permission to make digital or hard copies of all or part of this work forpersonal or classroom use is granted without fee provided that copies arenot made or distributed for profit or commercial advantage and that copiesbear this notice and the full citation on the first page. To copy otherwise, torepublish, to post on servers or to redistribute to lists, requires prior specificpermission and/or a fee.  ACP4IS’09,  March 2, 2009, Charlottesville, Virginia, USA. Copyright 2009 ACM 978-1-60558-450-8/09/03 ...$5.00. The package template (PT) mechanism [9, 10, 17] targetsthe development of collections of reusable interdependentclasses. Such a collection is a template for a package, thatmaybe instantiated atcompiletime,thusforminganordinarypackage. At instantiation, the template may be customizedaccording to its usage, and classes from different indepen-dent templates may be merged to form one new class.Inthisarticle,weutilizepackagetemplateswithacompar-atively (and intentionally) small and simple aspect orientedextensiontoprovideareusablepackagefortheObserverpat-tern, and compare our approach to the other solutions men-tioned above. 2. OVERVIEW OF THE PT MECHANISM We here give a brief and general overview of the packagetemplatemechanism. Theconceptsofthemechanismarenotinthemselvestiedtoanyparticularobject-orientedlanguage, but the examples will be presented in a Java-like syntax.A package template looks like a regular Java package, butwe will use a syntax where curly braces enclose the contentsof both templates and regular packages., e.g.: template T<E> {class A { ... }class B extends A { ... }} Templates may have (constrained) type parameters, such as E above,butthiswillnotbetreatedinanydetailinthispaper.Apartfromthisandafewotherconstructs(suchastheexten-sions we propose in Section 2.1), valid contents of a templateare also valid as plain Java programs. As such, templatesmay also be type checked independently of their potentialusage(s).In PT, a template is instantiated at compile time with an inst  statement, which has some significant differences from Java’s  import . Most notably, an instantiation will create alocal copy of the template classes, potentially with specifiedmodifications, within the instantiating package. An exampleof this is shown below: package U {inst T<C> with A => C, B => D;class C adds { ... }class D adds { ... } // D extends C since B extends A} Here, a unique instance of the contents of the package tem-plate  T  will be created and imported into the package  U . In itssimplest form, the  inst  statement just names the template to be instantiated, e.g.  inst T . The example above additionallyshowshowthetemplateclasses A and B arerenamedto C and 37  D ,respectively,andthatexpansionsaremadetotheseclasses.Expansions are written in  adds -clauses, and may add vari-ables and methods, and also override virtual or implementabstract methods from the template class.An important property of PT is that everything in the in-stantiatedtemplatethatwastypedwithclassesfromthistem-plate ( A  and  B ) is  re-typed  to the corresponding expansionclasses ( C  and  D ) at the time of instantiation (PT rules guar-antee that this is type-safe). Any sub/super-type relationswithin the template is preserved in the package where it isinstantiated.Another important property is that classes from different,possibly unrelated, templates may also be  merged  upon in-stantiation to form one new class. Consider the simple ex-ample below: template T {class A { int i; A m1(A a) { ... } }}template U {abstract class B { int j; abstract B m2(B b); }} Consider now the following usage of these templates: inst T with A => MergeAB;inst U with B => MergeAB;class MergeAB adds {int k;MergeAB m2(MergeAB ab) { return ab.m1(this); }} These instantiations result in a class  MergeAB , that containsthe integer variables  i ,  j  and  k , and the methods  m1  and  m2 .Note how the abstract  m2  from  B  is implemented in the  adds clause,andfurthermorehowboth  m1 and  m2 nowhavesigna-tures of the form  MergeAB → MergeAB . We shall use a similarconstruct in Section 3.To sum up, some of the useful properties of PT are: It sup-ports writing reusable templates of interdependent, cooper-ating classes which may be statically type checked withoutany information of their usage. Upon instantiation, a tem-plate class may be customized, and merged with other tem-plateclasses. Referenceswithinatemplatetoatemplateclasswill be re-typed according to the instantiation. 2.1 AOP Extensions We here extend the basic PT mechanism described abovewithaminimalsetofconstructsforaspect-orientedprogram-ming. Thus, we consider a restricted version of commonAOP concepts, that paired with the inherent possibilities inPTfore.g.mergingandexpandingclassesmayprovidesomeof the power and flexibility found in “traditional” AOP lan-guages.“Aspect-orientedprogrammingisquantificationandobliv-iousness” [5] is often seen as an imperative quote for whatAOP is or should be. However, in this paper we wish toinvestigate the possible benefits of a slightly different ap-proach. To begin with, aspects are realized not as separateentities (neither at runtime nor compile-time), but rather aspointcutsandadvicedefinedasmembersof(template)classes.In practice, this means that the possible changes to a baseprogram by a (conceptual) aspect will be limited in scope by a corresponding template instantiation. Furthermore, itmeans that the pointcuts are local to the defining class, in-cluding its subclasses (given the appropriate modifier), andthattheymayonlyrefertomemberswithinthedefiningclassor any of its superclasses, following the OO principle of en-capsulation. Hence, they may also be refined or redefined by subclasses or in addition clauses. The same will apply toadvice.This may seem like a severe restriction compared to e.g.AspectJ, but we believe that paired with the flexible tailoringmechanisms offered by PT (in particular the merging possi- bilities), this provides a sufficiently powerful and expressiveconstruct for many purposes.Given that all pointcuts will refer to local members,  quan-tification usingwild-cardsovermemberortypenamesshouldnot be as necessary as in e.g. AspectJ. Therefore, we find itworthwhile to explore the disallowing of wild-card usagewith regards to member names, and rely instead on explicit joinpointspecification. Thiswillresultinamechanismwherethe pointcuts do not specify a pattern to be matched against join points, but instead an actual  binding  to join points.Pointcuts are declared inside classes according to the fol-lowingEBNFgrammarsketch,wheretermsinquotesareter-minals and the unquoted ones are non-terminals. Produc-tions that are equal to their Java equivalents are left out for brevity, and things enclosed in  <<  and  >>  should be under-stood as “pseudo-EBNF” in place of parts left out: pc_decl ::= { modifier } "pointcut"( qualified_identifier | "void" | "*" )identifier "(" [ <<parameter list>> ] ")"( "{" pc_expr "}" | ";" )pc_expr ::=( call | execution | get | set ) "(" identifier ")"| pc_expr "&&" pc_expr| pc_expr "||" pc_expr | "(" pc_expr ")" Likewise, an advise has the following syntax: advice_declaration ::={ modifier } "advice" identifier( before | after | around ) identifier"{" { <<valid statement>> } "}" Tokeepthisexpositionshort,wewillnotdiscusstheEBNForthe generated language in any further detail, but rather turnto a small example to illustrate how the mechanism works.Consider the following template: template T {class A {A m1(A a) { ... }pointcut A pc1 (..) { call(m1) }}} The class  A  contains a pointcut that matches calls to the themethod  m1 . The template may be instantiated as shown be-low: inst T with A => B;class B adds {pointcut B pc1(..) { call(m1) || call(m2) }advice afterM after pc1 { ... }B m2() { ... }} Here,  A  is re-typed to  B , and  B  adds a method and an advice,and refines the pointcut  pc1 . Note that even though  pc1  in T  refers to the type  A , the pointcut will after instantiation re-fer to  B , and continue to match method calls to  m1 , and thiswould hold even if   m1  was renamed in the instantiation. Thisis made possible by the fact that pointcuts are not string pat-terns, but actual bindings, as mentioned above. 38  Subject  -observers : List<Observer>templateObserverProtocol Line templateDrawing a)b) + addObserver(in o: Observer) # pointcut* changed (..) # advice after changed Observer  + setStart(in x : int, y: int)+ setEnd(in x : int, y : int) Screen + notify(in s: Subject) + update(in l: Line) Figure 1:  a) The Drawing template, and b) the roles of theObserver pattern 3. THE OBSERVER PATTERN EXAMPLE Theobserverpatternisadesignpatternwithtworoles,thesubject and the observer. Each subject maintains a list of ob-servers that are interested in being notified when certain (yetunspecified) changes occur in the subject. An observer maychoose to observe one or more subjects at any given time. 3.1 Single Subject/Single Observer Classes To exemplify the use of the pattern, we consider a pack-age for drawing objects on a screen (strongly resemblant of the examples in [8] and [11]). In this section, we look at anexample with only two classes,  Screen  and  Line , as shownin Figure 1a. When a line changes its length or position, thescreen should be updated to reflect the changes. We wouldlike this logic to be abstracted out of the concrete  Screen  and Line  classes, so that it can be reused for other manifestationsof this particular problem.Utilizing package templates, we could implement the Ob-serverpatternasshowninFigure1bwiththefollowingcode 1 : template ObserverProtocol {public abstract class Observer {abstract void notify(Subject changee);}public abstract class Subject {List<Observer> observers = new List<Observer>();public void addObserver(Observer o) {observers.add(o);}abstract protected pointcut * changed(..);protected advice ac after changed {foreach(Observer o in observers) { o.notify(this); }} } } The Observer classhasonlyonemethod,theabstract notify ,that will have to be implemented at a later stage to make itmeaningful to the concrete observers.The subject class has methods for adding and removingobservers from the current subject instance. Furthermore, itdefines an abstract pointcut  changed , that will have to be re-fined in concrete subjects. The pointcut specifies that any pa-rameters and return types are valid for its (as of yet unde-fined) corresponding join points with the use of wild-cards“ * ” and “ .. ”. Using the drawing package of Figure 1a, wecould do the instantiation as follows: inst Drawing withScreen => ScreenObserver, Line => LineSubject; 1 The removeObserver method is trivial and hence omittedforbrevity. Theclasses inthediagrams containafourth com-partment for AOP related members where this is relevant,and the names of abstract classes and members are writtenin italics. LineSubject ScreenObserver -observers : List<ScreenObserver>+ addObserver(in o: ScreenObserver)+ setStart(inx : int, in y : int)+ setEnd(inx : int, in y : int)*+ notify(in s: LineSubject)+ update(in l: LineSubject) ..# advice after changed Figure 2:  The resulting classes from the merge of the ObserverProtocol andDrawing templates Line template Drawing Point +   setStart(in   x   :   int,   y :   int)+   setEnd(in   x   :   int,   y   :   int) Screen +   setPos(in   x   :   int,   y :   int) Printer +   display(in s   :   String) +   print(in s   :   String) Figure 3:  The drawing template with two potential subjects and two po-tential observers inst ObserverProtocol withObserver => ScreenObserver, Subject => LineSubject; ScreenObserver  and  LineSubject  become a merge of   Ob-server and Screen ,and Subject and Line ,respectively. Fur-thermore, we make use of   adds  clauses to concretize the ab-stract members of the template classes. These clauses areonly needed in order to concretize what was left as abstractin the respective templates. class ScreenObserver adds {void notify(LineSubject l) { update(l); }}class LineSubject adds {pointcut * changed(..) {call(setStart) || call(setEnd)}} The two resulting classes are shown graphically in Figure 2.Notethatthatare-typinghasoccurred,suchthatforinstancethe  addObserver  method now takes a  ScreenObserver  as itsonly parameter. Similarly, the  notify  method now takes a LineSubject  parameter. Due to the retyping done by the PTmechanism, no casts are required. 3.2 Multiple Subject and/or Observer Classes In the previous section we looked at a rather simple sce-nario in which there was only one class having the subjectrole, and one having the observer role. In this section, wewill look at the more general problem, with multiple classesplaying the roles of subjects and observers.To exemplify, we extend the template  Drawing  such thatthere are several classes acting, respectively, as subjects andobservers, and hence we need to modify our instantiationcode a little. The good news, however, is that our implemen-tation of the Observer pattern itself needs no modification.The classes of the new  Drawing  template are shown in Figure3. Applying our Observer pattern to this package, we woulduse the following instantiation: inst Drawing withScreen => ScreenObserver, Printer => PrinterObserver,Line => LineSubject, Point => PointSubject;inst ObserverProtocol withObserver => BaseObserver, Subject => Figure; 39  Figure ‐  observers   :   List<BaseObserver>+   addObserver in o   :   BaseObserver #     pointcut *   changed    (..) #   advice   ac after   changed LineSubject PointSubject +   setStart(in   x   :   int,   y :   int)+   setEnd(in   x   :   int,   y   :   int)#   pointcut *   changed   (..)+   setPos(in   x   :   int,   y :   int)#   pointcut *   changed   (..) BaseObserver  +   notify(in s   :   Figure) ScreenObserver +   display(in s   :   String)+   notify(in s   :   Figure) PrinterObserver +   print(in s   :   String)+   notify(in s   :   Figure) Figure 4:  The resulting classes from the merge of the ObserverProtocoltemplate and the new version of the Drawing template The  Drawing  template does not contain any base classes intowhich the required functionality for subjects and observerscan be merged. However, PT allows us to  introduce  super-classes directly, in a type safe manner, as shown in the in-stantiations above and the addition classes below: abstract class BaseObserver adds {}class ScreenObserver extends BaseObserver adds {void notify(Figure f) { display(f + " has changed"); }}class PrinterObserver extends BaseObserver adds {void notify(Figure f) { print(f + " has changed"); }}abstract class Figure adds {}class LineSubject extends Figure adds {pointcut * changed(..) {call(setStart) || call(setEnd)}}class PointSubject extends Figure adds {pointcut * changed(..) { call(setPos) }} The resulting package is shown in Figure 4. Note how thefunctionalityfortheSubjectandObserverrolesaredistributedto their respective functional counterparts from the  Drawing packagethroughthenewlyintroducedcommonsuperclasses. 4. RELATED WORK The srcinal description of the Observer pattern by the so-called “Gang of Four” (GoF) found in [6] comes with an ex-ample implementation in C++. This implementation makesuseofC++’ssupportformultipleinheritancetoprovidesub- ject and observer classes for the pattern. Since we are target-ing Java in this example, such inheritance hierarchies cannot be used.TheGoFversionalsomakesuseofaspecial“trick”inwhichthe observer knows the identity of the (single) subject forchanges in which it is interested. Because of this they canavoid having to do a type cast that would have been neces-sary in the general case.In DesignPatternImplementationinJavaandAspectJ  [8],Han-nemann and Kiczales implement the design patters from theGoF in AspectJ, and contrast these implementations to theirpotential pure Java counterparts. The Observer pattern isused as a running example. In their implementation, the pat-tern is handled by one single abstract aspect, the  Observer-Protocol . This aspect will exist as an entity at runtime, andcontains a global map of observers to subjects. This is in con-trast to the PT version, in which there is no notion of the as-pect as a separate entity at runtime, but rather that the rolesof the aspect are imposed on (and localized in) the classesthat are to play the respective roles.TheAspectJaspectdefinesempty“marker”interfaces,thatconceptually declare the existence of the two roles of the pat-tern. However,sincetheinterfacesareempty,theaspectdoesnotmakeitexplicitwhichoperations/behaviorsbelongtoei-ther participant in the pattern.The abstract aspect is concretized via inheritance as con-crete aspects, that override the abstract pointcut  subject-Change , and define which concrete classes should be desig-natedaseithersubjectorobserverthroughtheAspectJ decl-are parents  construct. Different concrete aspects may be de-fined based on the abstract aspect. In order to update the ob-server(s)whenachangehasoccurred,aruntimecastmustbemade from the interface  Observer  to the class  Screen , sincethe interface is empty and hence has no knowledge of the Screen ’s  display  method.In PT, the concretization happens through the  inst  state-ment (with optional addition classes for e.g. concretizing ab-stract members). The retyping may in many cases eliminatethe need for casts completely.Thefactthatpointcutsarenotexplicitlyboundtojoinpoints,means that the AspectJ version is more prone to errors stem-ming from faulty pointcuts (the  fragile pointcut problem  [16]),while the PT version sacrifices some flexibility for a greaterdegree of relative pointcut safety.  AspectC++  [15] adds full-blown AOP support to the C++language, including support for templates and non-OO pro-gramming. Contrary to our approach, the authors explicitlystate as one of their goals to  “not make any compromise regard-ing obliviousness and quantification” , i.e. to not put any restric-tions on their language with regards to the definition from[5]. This provides for a very powerful paradigm, which pairsup well with the nature of C++ itself.One of the examples in this article shows a reusable Ob-server pattern implementation. It makes use of C++’s mul-tiple inheritance capabilities and a distinguishing join pointAPI to add the required members to the classes designatedas subject and observer, respectively. The solution is simi-lar to ours in the respect that only pointcuts and the updatemethod for the observer need to be concretized for a partic-ular utilization, yet differ in the sense that while our imple-mentationsyntacticallydistributesthesememberstotheirre-spective classes, the AspectC++ version keeps everything ina central aspect.In  Conquering Aspects With Caesar  [11], Mezini and Oster-mannshowanalternativeimplementationofthepattern,uti-lizing the CaesarJ system. The work mainly addresses twopointswithregardstotheAspectJimplementationdiscussedabove; (I) the need for expressing aspects not as a monolithicentity, but rather as a set of interrelated, interacting modules,and (II) the need for flexible and reusable aspect bindingsand implementations.With respect to (I), Caesar achieves this through so-called aspect collaboration interfaces  (ACIs). The ACIs are hierarchi-cal, and may contain several (related) sub-interfaces. Eachinterface may describe a set of provided and/or required op- 40
Search
Similar documents
View more...
Related Search
We Need Your Support
Thank you for visiting our website and your interest in our free products and services. We are nonprofit website to share and download documents. To the running of this website, we need your help to support us.

Thanks to everyone for your continued support.

No, Thanks