Application specific design patterns and the integration of Batory's work with Demeter. (based on discussions with Mira) ============================================================= An application specific design pattern describes a group of collaborating classes who collaboratively achieve some application specific goal. Visitors are one kind of executable application specific design pattern. A second kind of application specific design pattern is needed: Adjusters (see the early work of Holland on programming contracts and Mezini's and Batory's recent work). Adjusters allow us to talk about coordinated class refinement: For example, when we add a method f to class A, then we add a method M to class C where M depends on both f and A. An example of a domain specific design pattern would be a JOURNALIZER, a mechanism to record and replay events (proposed by Dan Port). This pattern describes a collaboration between a set of recordable classes in which certain events are forwarded to a journal class for recording. The way the recordable classes and the journal are connected is left open by the pattern. The JOURNALIZER pattern is described by an adjuster as follows: Adjuster JOURNALIZER (Strategy s = {-> {Class1 Class2} Journal}) { at Class1 { void event1() { do s { // omitted during at s.target { when-visiting {host.event1();}}} // maybe we can use some syntactic sugar here // do s is a statement added to the OO language next(); } void event2(Par p) { do s { //ommitted during at s.target { when-visiting {host.event2(p);}}} next(); } at Class2 { ... } } at Journal { event1() { mystructure.register("Class1.event1" ); } event2(Par p) { mystructure.register("Class1.event2", p); } } } We have two kinds of application-specific design patterns both being class graph modifiers: adjusters and visitors. Both may be parameterized by strategies. An adjuster modifies a group of classes permanently and is used outside the scope of a strategy. A visitor also modifies a group of classes but the modifications are only felt during some traversal. A visitor is used in the scope of a strategy. Adjusters may be composed and may communicate with adjacent adjusters and visitors may be composed and may communicate with adjacent visitors by calling methods of adjacent visitors and by accessing the return values of adjacent visitors. We already have adjusters in Demeter/Java: C { int f() to Z (V); } is an adjustment which extends only class C with f. We are going to write this as: Adjuster Adj { at C { int f() to Z (V); } } modify by Adj; // will modify class graph by adding f to C With Adjusters we can now do Batory's example in the following way: From Implementing Layered Designs with Mixin Layers by Batory et al. http://www.cs.utexas.edu/users/schwartz/pub.htm Adjuster BINTREE { at Node { Node parent_link; Node left_link; Node right_link; // local methods } at Container { Node header; void insert(EleType el) { ... } void erase (Node node) { ... } boolean find (EleType el) { ... } } } Adjuster TIMESTAMP { at Node { Time creation_time; Time update_time; boolean more_recent (Time t) { ... } } at Container { Time update_time; boolean find_newer(EleType el, Time t) { ... } void insert (EleType el) { ... } // other time-related methods } } Adjuster SIZEOF { at Container { int count; init (@ count = 0; @) void insert (EleType el) {next.insert(el); count++;} void erase (Node node) {next.erase(el); count--;} int size () {return count; } } } Class graph: Node = EleType. Container =
Node. modify by SIZEOF_Visitor uses TIMESTAMP_Visitor uses BINTREE_Visitor Mira was pointing out that we should parameterize adjusters with strategies For example, the BINTREE adjuster from above, we would like to parameterize: Adjuster BINTREE (Strategy s) { at Node { Node parent_link; Node left_link; Node right_link; // local methods } at Container { Node header; void insert(EleType el) { ... } void erase (Node node) { ... } boolean find (EleType el) { ... } // new void traverse() { do s1 = s intersect {->Container Node bypassing -> *,parent_link,*} { at Node { host.print(); next();} } } } Now modify by SIZEOF_Visitor uses TIMESTAMP_Visitor uses BINTREE_Visitor({-> Container Node bypassing -> *, right_link,*}) gives us a binary tree with left-most branch traversal only. Remember that intersection of strategies is supported by the strategy paper. Mira has another example: Adjuster PERFORMING_TRANSACTIONS (Strategy s = {-> Account Logging}) { at Account { void deposit(Amount a) { do s { // omitted during at s.target { when-visiting {host.deposit(a);}}} // maybe we can use some syntactic sugar here // do s is a statement added to the OO language balance = balance + a; } void withdraw(Amount a) { do s { //ommitted during at s.target { when-visiting {host.withdraw(a);}}} balance = balance - a; } } at Logging { deposit(Amount a) { mystructure.register("deposit", amount); } withdraw(Amount a) { mystructure.register("withdraw", amount); } } } modify by PERFORMING_TRANSACTIONS We could have a cd: Account = Logging. or Account = Info. Info = Logging. An adjuster does not use "during" at the outer level, only inside methods. A visitor uses "during" at the outer level An adjuster and visitors work together like conductor and an orchestra. The adjusters define permanent modifications of the key classes and the visitors help to produce the "music" by doing the detailed work. Adjuster X (Strategy s) { at A { void f() { do s (V1); x = x + 1;} } } Visitor V1 (Strategy s) { during s { at D { when-visiting { ... } } at E { when-visiting { ... } } } } We view adjusters and visitors as the key building blocks of implementations of aspect languages. We have collaboration between adjusters: next.f() calls f in the next adjuster. We have collaboration between visitors: next_visitor.get_state() gives current state of next visitor Mitch proposed this name over next.result() next(): calls code of next visitor. We have extended the usefulness of strategies: They are used in visitors to express traversal-based algorithms in a structure-shy way. They are also used in adjusters to express collaborative refinement in a structure-shy way. -- Karl