From mira@ccs.neu.edu Wed Nov 19 19:17:36 1997 Received: from taboor.ccs.neu.edu (mira@taboor.ccs.neu.edu [129.10.112.115]) by amber.ccs.neu.edu (8.8.6/8.7.3) with ESMTP id TAA16902 for ; Wed, 19 Nov 1997 19:17:35 -0500 (EST) From: Mira Mezini Received: (mira@localhost) by taboor.ccs.neu.edu (8.8.6/8.6.4) id TAA18432 for lieber; Wed, 19 Nov 1997 19:17:34 -0500 (EST) Message-Id: <199711200017.TAA18432@taboor.ccs.neu.edu> Subject: Dem/J - Rondo To: lieber@ccs.neu.edu (Karl Lieberherr) Date: Wed, 19 Nov 1997 19:17:34 -0500 (EST) X-Mailer: ELM [version 2.4 PL23beta2] MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Status: RO Hi Karl: I had a look at the discussions in /adaptive/www/related-work/Rondo/ It seems like you have already put everything there. Here is something more that I had written at the beginning of our discussion, but never sent it to you. You may want to consider and add it to the other discussions. -- Mira ------ cut here ------- On the integration of Rondo concepts into Demeter/Java ------------------------------------------------------ 1. Why integrate? How do both models fit together? What can they profit from each other? --------------------------------------------------------------------------------------- Demeter/Java (DJ) and Rondo (R) share a similar philosophy however for achieving two complementary purposes: In order to remove the structural details from the behavior, DJ split a certain functionality in strategies and visitors. The former specify the succinctly the structural details, while the latter fill this succinct specification with behavior. *** To achieve adaptiveness with regard to changes in the structure the separated structure and behavior specifications are loosely coupled. *** A similar philosophy underlies R, except that now the focus is on making software more robust of variations on the behavior and not on the structure. *** Again the goal is achieved also the separation of concerns: behavior is separated from behavior variations. The adjustment programs are also loosely coupled to the default classes. *** Thus, since they have a similar philosophy, but focus on "disjoint" goals, both models could complement each other very well. So, what can R profit from DJ? a) the structure-shy specifications and the whole code generation technology behind DJ. Structure-adaptiveness is not considered in R. b) it will leave in a concrete setting -- its concepts could be better tested. c) a further profit would be to use the strategies for specifying the range of a behavior modification in a system of collaborating objects. "Objects are not islands". In providing a functionality they cooperate with otehr objects, generally those that they "know", also called the acquintance objects. When in a certain context, one can imagine that a set of collaborating objects, not a single one, need to adjust their behavior. Consider a very trivial example of a graphical system consisting of entities like Lines, Polygons, and Planes. Suposse that a there is a certain plane P, such that any line that enters the area of the plane while being moved, should turn to a fixed-slope line, i.e. one the slope of which cannot change. If however, the line turning temporarily to a fixed-slope line is part of a polygon PL, then the latter should also adopt a context-dependent behavior variation -- being non-rotable; if a polygon rotates, then all its lines has to change their slope. Specifying the set of objects in an object graph the behavior of which should be affected by the "entrance" into a certain context, could be done by strategies. The class graphs should be extended with new kinds of lines in this case. We use the term "raising scope" for describing this role of strategies. Now, what can DJ profit from R? a) DJ does not provides means for general (dynamic) behavior variations as those represented by the strategy or state patterns. This could be broght into DJ by Rondo. b) Adapting the Rondo style for implementing visiting functionality over a structure of objects that is specified by a strategy would simplify the DJ style, since no before and after methods are needed anymore. For more details and some examples see the e-mail discussion below: ------------- >From lieber@ccs.neu.edu Fri Oct 31 15:28:46 1997 Received: from stockberg.ccs.neu.edu (lieber@stockberg.ccs.neu.edu [129.10.112.123]) by amber.ccs.neu.edu (8.8.6/8.7.3) with ESMTP id PAA18366; Fri, 31 Oct 1997 15:28:45 -0500 (ES T) From: Karl Lieberherr Received: (lieber@localhost) by stockberg.ccs.neu.edu (8.8.6/8.6.4) id PAA09628; Fri, 31 Oct 1997 15:28:45 -0500 (EST) Date: Fri, 31 Oct 1997 15:28:45 -0500 (EST) Message-Id: <199710312028.PAA09628@stockberg.ccs.neu.edu> To: mira@ccs.neu.edu Subject: Re: same model Cc: lieber@ccs.neu.edu Status: RO Hi Mira: a very useful mail I think. This brings us a big step further. >From mira@ccs.neu.edu Thu Oct 30 15:31:45 1997 >From: Mira Mezini >Subject: Re: same model >To: lieber@ccs.neu.edu (Karl Lieberherr) >Date: Thu, 30 Oct 1997 15:31:42 -0500 (EST) > >Hi Karl: > >> After a second thought, your proposal today matches the one below? >> Except that you don't view a context class as a class. > >It is clear that both models have similiraties, however they are not >the same. As I told you, the context relation and the Rondo >model share many things. However, there are two points to make >here: > >1. *** >------ > >There is a difference between the contex relation as described >in Linda's thesis (and TSE paper), and the current realization >of the realisation in Demeter/Java. The description is more general >than the current realization. Currently, context objects are I agree. >used only in combination with strategies to specify some behavior >that happens during traversing an object structure. This is also >reflected in the structure of context classes below: > >> Context classes should have the following structure: >> >> Context Class V { >> instance variables >> methods >> // several redefinitions: >> redefines C { // vertex based redefinition >> // additional instance variables >> D d; // not needed for attachment to objects >> // only for attachment to calls >> // overwrite or add new methods for C-objects >> f(A) {}; >> // incremental methods for C-objects >> before {}; >> after {}; >> around {}; >> // incremental methods for edges from C-objects >> before l {} >> after l {} >> around l {} >> // includes also "derived" edges (methods) >> before x(A) {} >> after x(A) {} >> around x(A) {} >> } >> redefines -> X,y,Y { // edge based redefinition >> // additional instance variables for source of edges >> // overwrite or add new methods for source of edges >> f(A) {}; >> // incremental methods for y-edge >> before {}; >> after {}; >> around {}; >> } >> >This structure subsumes the existence of before(), after() and >around() methods in the base class structure which is being modified, >right? These methods are actually present in all classes that are You are right. This generalization of context objects is geared towards traversals. >affected by a strategy (i.e. included in the computed strategy graph) >-- they are automatically generated by the >Demeter/Java preprocessor as empty methods which are called >by other methods whose implementation contains a strategy, right? right. >So, I totally agree with: > >> What is new here? >> >> DYNAMIC EXTENSION OF CLASS STRUCTURE for duration of a task. >> This removes the pollution of the class graph with information >> which is only needed for a specific task. > >But, what about modifying the behavior of classes whose behavior >definitions in the .beh files do not include strategies, i.e. >classes whose methods are simple Java code? >What about an account object which becomes dynamically >``saving''? There is no traversing involved here. Your examples >always involve traversing an object structure and iteratively >performing some task at each node. But. try to think in terms >of other examples. What about the distrubuted object which >at a certain moment because of some measurements of the network >resource availability shoud change its implementation? Certainly, we want to deal with that too. Often we want to change the implementation of a group of collaborating objects. But we don't want to commit to a detailed organization of the collaborating objects when we write the collaboration. > >> New parts and functions may only be used in redefinition. >> Either all incremental or all overrwriting. > >What does ``all incremental or all overrwriting'' mean? What is ``all''? >Could you explain this in more detail? > Let's see whether I can reconstruct what I said there. In Demeter/Java, when you add more visitors to a traversal, their methods are all incremental. Linda and Walter promoted an overriding model where methods at super classes override methods at subclasses. // Demeter/Java code // Manager a subclass of Employee int average() to {Salary, Employee, Manager} (Average, Count, Sum); at both Employee and Manager we have two before visitor methods and both will be called. in an overriding model only the one at Manager would be called. Mixing incremental and overrriding would be confusing. I think incremental is what we want. >2. *** >------ >The second point is as follows. Let us consider now the more general >description of the context relation (as described in Linda's >thesis or TSE paper), which is able to deal with both dynamic variations aka >strategy (or state) pattern and variations aka visitor pattern. >In this form the context relation is much more similar to the I agree. >Rondo style. Still, I have the impression that Rondo is slightly more general. >Let me explain why. > >Your contexts are objects, while my adjustments are classes (more >precisely class-like constructs). As a qonsequence, the dynamic >modification implied by the context relation results in a kind >of delegation-based style. Is that right? Here is some sample code from your >TSE paper implementing the example used for illustrating >the strategy pattern in GOF book: > > public class Composition{ > public void instance compose() {this.draw}; // <-- default implementation > public void draw() {...}; //some implementation > > ... > > public class TextCompositor { // Note this is a context class > ---------------------------- (*) > public void dosomething() {...} //some implementation > update class Composition { > void compose() {context.dosomething(); this.draw();} > --------------------- (**) > > ... > ... > > Composition c = new Composition() > TextCompositor t = new TextCompositor(); //create context object > --------------------- (***) > c::=t; > >(*) the structure of this context class as decribed here looks very different from the >structure of a context class as described above. There are no before and after methods >here. This is similar to Rondo's adjustments. That confirms my understanding. > >(**) (***) doSomething does something for the composition object ``c''. >Why should this method be sent to the context object? >This is a functionality the base object should now >additionally provide. We want to modify the behavior >of an existing object. What's the point of creating a new object here? >You would say: well, we need to encapsulate >somewhere the new state that TextCompositor may declare. >Definitely, but the new state is (a new) part of the existing >base object in the new situation (i.e. when >it uses a non-default strategy for composing). >That means the new state should be visible for the extended >functionality of the existing ``c'' object, but not >externally. It is ``c'' that now is (temporarily) ``biger'', >but there is no reason to create a new object. > I am not married to the context objects. I am ready to give them up for something better. > >It seems to me that: context attachement == bind the ``context'' identifier to >the cretated context object ''t''. >Than context.dosomething() is delegation, i.e. >``this'' should be implicitly passed along. > > >In Rondo this would look like: > > public class Composition{ > public void compose() {this.draw}; // <-- default implementation > public void draw() {...}; //some implementation > > adjustment TextCompositor { //Everything that is in a subclass can be put here > public void dosomething() {... this....} //some implementation. > //can refer to any method > // in the base class. > void compose() > {this.dosomething(); super.compose();} > //Note, ``this'' is a composition object > } modifies Composition when: #text. > > > ... > > > Composition c = new Composition(); > c.raise(#text); // Classes are attached here not objects. > --------------------------------------- > c.compose; > > ... //still text > > c.undo(#text); > > >At this point, let me summarize this long mail. >The integration of Rondo in Dem/J would mean: > >a) Realization of a more general dynamic behavior modification mechanism in >DEM/J, similar to the one intended by the context relation in its general >form, i.e. in addition to the restricted form currently implemented with the >context objects in DEM/J. This is similar to Rondo, but is missing in >the current implementation of DEM/J. Compared to context attachement, >Rondo applies modifications by class, rather than object attachments. > In Ian Holland's thesis he programs with contracts which are expressed in terms of objects (participants) and their obligations. Such a contract has also the flavor of an adjustment. Do you have more insight into the connection? Can we do with adjustments all we can do with contracts or is something missing. > >b) If we want to go further, adjustments could be used instead of >context objects even for traverse-based functionality. The name, >context or adjustment is not important, the point is rather at >the way structure and behavior are connected together. What I mean is >the following. Demeter/J compiler will as currently generate the >traversing code within a method whose implementation in >.beh file has a strategy specification. However, no empty before or >after methods needs to be inserted. >In this case, the .beh file would looks like: > > class Foo { > > method foo_1 { > strategy from X to Y} > } > > > adjustment Count { > method foo_1 { ... do something ...} > } > > > ... > > > Foo f = Foo new(){Count}; > f.foo_1; Foo { int count() to Y (Count); Count { (@ int total; @) init (@ total = 0; @) before Y (@ total++; @) return (@ total @) } Foo f = new Foo(); f.count(); still looks more natural to me. But let's follow what you say below: Foo { int count() to Y; } adjustment Count { (@ int total; @) init (@ total = 0; @) int count() { before Y (@ total++; super.count(); @) } return (@ total @) } Foo f = new Foo() with Count; f.count(); This looks not bad but we have the additional coupling with the function name. But that is handy in other situations. The strategy seems to be to get rid of attaching attachemnts to calls, but to only attach to objects. That is what contracts do. > >Disadvantage: - consistent naming of functions is important for adjustments to work. > >However, when looking at examples in the Demeter Homepages, e.g. in the >page titled "Demeter/Java: Adaptive Programming with traversals and visitors" >by Doug and you, one notices that also in context classes names of the >class or method which the context class modifies are stated explicitly. >When a context class modifies more that one method of >the class x, one should say which method in the base class a certain before >method modifies: > > > class Y { > > Y_M1 { > ... some traversal ... }; > > Y_M2 { > ... some traversal ... }; > } > > context class X { > .... > > updates Y { > > before Y_M1 {...}; > before Y_M2{...}; > > ...} >Is that right? yes, I checked this in the above example. > >That means, even with the before() and after() methods there are explicit >comittment in the code (in the form of class or method names) about >which context objects can be used with which classes. certainly. >Consider the piece of behavior from one of demeter/java pages >(http://www.ccs.neu.edu/research/demeter/posters/introDemeterJava/poster.html) > > >Computer { > traversal allEquip(PricingVisitor price, InventoryVisitor inv) { > bypassing -> CompositeEquipment,parts,* to Equipment; > } >} > >PricingVisitor { > before Equipment (@ total = total.add(host.get_netPrice()); @) > // Chassis are on sale... > before Chassis (@ total = total.add(host.get_discountPrice()); @) >} > >InventoryVisitor { > before Equipment (@ inventory.accumulate(host); @) >} > > >The PricingVisitor context can be only used as it is with a class >structure that has Equipment and Chassis. This cannot be avoided. >Nothing wrong about that. What I want to say, is that in one or th >other form you have some connecting points in the code, being >the names of the messages or classes. Yes. With adjustments we need to connect both with classes and method names? See count example, above. >While a corresponding adjustment based implementation as in my thesis >only assumes the existence of accept() methods, in addition to netPrice and >discountPrice(). >You could define both adjustments in the pricing functionality in a very >generic way: > >a) naming them simply Pricing and composite pricing (i.e. withput >the prefix Equipment or CompositeEquipement). > >b) separating the modifies sentences from the definition of >the adjustment. > >These could then be reused for all class structures that >has netPrice and discountPrice and an accept method. > >However, these are not substantial things. One would even keep the >current implementation of visitors, and maybe implement the new way >via adjustments in addition for testing purposes. The important >point is the general case of modifications. > >I guess, that's enough for one mail :-) Let us continue the discussion >in person or via forthcomming mails. > >-- Mira > > You express your ideas very clearly. -- Karl ------------------------------------------ The following example is part of a discussion with Karl. Suppose we want to traverse a class graph from a class Foo to the class Y, and compute the average of the values of the attribute v of Y-objects everywhere we encounter Y-objects during the traversal. This kind of visiting functionality would be implemented (more or less) as follows in Rondo style: class Foo { int avg() {to Y}; } adjustment Sum { int total; void initialize() int {total += this.get_v}; } adjustment Count { int cnt; void initialize() {...}; int {cnt++a;} } adjustment Average { {printout this.total/this.cnt}; } Sum modifies Foo when: #summing; Count modifies Foo when: #counting; Average connects Sum Count when: #averaging; Foo f = new Foo(){averaging(avg)}; f.avg();