• Aucun résultat trouvé

OBJECT TRAVERSAL AND TRANSPORTATION

Adaptive Software by Example

5.3 OBJECT TRAVERSAL AND TRANSPORTATION

Basket

contents

Apple

weight

Orange SeveralThings

Thing

DemNumber Weight

v

Fruit

Figure 5.20: Nested baskets

class dictionary has the property that every cycle is well behaved in a sense that is made precise later (see the chapter on style rules for class dictionaries, Chapter 12). Intutively, a cycle is well behaved if there is an edge exiting the cycle.

The propagation pattern from Fig. 5.5 does not need updating, except the already familiar renaming of Container to Basket; the same program will work for the class dictionary for nested fruit baskets. This shows the power of propagation patterns as well as the power of delayed binding of calls to code. When the function add weights is called for an element of SeveralThings, the class of the element will determine whether the function for Basket or Fruitwill be called. All the functions of classes that describe alternatives, such as Thing and Fruitare virtual functions.

5.3 OBJECT TRAVERSAL AND TRANSPORTATION

Next we consider a more interesting propagation pattern. Let's assume that we need to nd all Apple-objects contained in a Basket-object of a given Household-object. We do not care about the apples that are not in a basket. The following propagation pattern solves the problem.

*operation* void apples_in_basket()

*traverse*

*from* Household *via* Basket *to* Apple

*wrapper* Apple

(@ this -> g_print(); @)

130 CHAPTER5. ADAPTIVESOFTWAREBYEXAMPLE

Basket = <contents> SeveralThings.

SeveralThings ~ Thing {Thing}.

Thing : Basket | Fruit.

Fruit : Apple | Orange *common* <weight> Weight.

Apple = . Orange = .

Weight = <v> DemNumber.

Figure 5.21: Nested baskets, textual form

The via clause in the propagation directive forces the traversal through class Basket as desired. Instead of using

*from* Household *via* Basket *to* Apple

we can use equivalently a through clause (assuming Basket is a construction class)

*from* Household

*through* -> Basket,*,*

*to* Apple

The through clause forces paths through relationships. Here we force the paths through at least one construction relationship that starts at Basket. The expression->Basket,*,*is an edge pattern that describes the set of construction edges starting at class Basket. Edge patterns are used to make the software more exible by minimizing dependency on the class dictionary.

Next we only want to print apples that are not in a refrigerator. This is achieved by the following propagation pattern:

*operation* void not_in_refrigerator()

*traverse*

*from* Household

*bypassing* -> Refrigerator,*,*

*to* Apple

*wrapper* Apple

(@ this -> g_print(); @)

The bypassing clause makes paths avoid certain relationships. Here any construction relationship that starts at Refrigerator will be bypassed. Again an edge pattern is used to minimize dependency on the class dictionary.

To illustrate wrappers with prex and sux code fragments, we want to print the total number of apples in every refrigerator contained in a household.

5.4. SUMMARY 131

*operation* void count_apples_in_refrigerator()

*wrapper* Household

(@ int s = 0; this -> count(s); @)

*operation* void count(int& s)

*traverse*

*from* Household

*via* Refrigerator

*to* Apple

*wrapper* Refrigerator

*prefix*

(@ int in_frig = s; @)

*suffix*

(@ in_frig = in_frig - s;

cout << "Apples in refrigerator ="

<< in_frig; @)

*wrapper* Apple (@ s = s + 1; @)

The default traversal code for Refrigerator is wrapped with the prex and sux wrapper.

The sux wrapper uses the shift operator<<to produce output. cout is the output stream.

Further details on the stream classes are in your C++ book.

Transporting objects to the right locations is an important subtask in object-oriented programming. To illustrate how we can transport objects independently of detailed class structure knowledge, we consider the following problem that we solve through a transporta-tion directive.

For every apple in a household, print the apple information and the address of the household containing the apple.

HouseholdApple = *from* Household *to* Apple

*operation* apple_address()

*traverse* HouseholdApple

*carry* *in* Address* a *along* HouseholdApple

*at* Household (@ a = address @)

*wrapper* Apple

(@ cout << this << a; @)

The carry statement introduces the transportation directive and denes a local variable called a that is loaded with an address of a Household-object at class Household and that is used at class Apple.

5.4 SUMMARY

We have shown how adaptive programs are used and how they can be customized by class dictionaries. First we showed dierent ways of customizing a propagation directive with increasingly more complex class dictionaries. Then we turned to more complex propagation

132 CHAPTER5. ADAPTIVESOFTWAREBYEXAMPLE

patterns, showing several ways to control paths and to annotate object traversals. Two important properties of propagation patterns have been demonstrated: adaptiveness and extensibility.

We have shown the transition from objects to class dictionaries and to complete C++

programs. We demonstrated the evolution of a C++ program through an adaptive program that is applied to more and more complex objects. This incremental development is typical for object-oriented programming, and adaptive software allows us to automate some of this incremental development through customization. Although we used C++ as the program-ming language, a similar approach can be used with any other object-oriented programprogram-ming language.

Two questions that we have left open is how we nd the objects and in which order we use them to grow the system in small, easy-to-test steps. We will study those questions in the context of program evolution in the chapter on propagation patterns.

5.5 EXERCISES

Exercise 5.1

Consider the following class dictionary BASKET:

Basket = <nested> NestedBasket.

NestedBasket = <contents> SeveralThings.

SeveralThings : None | OneOrMore.

None = .

OneOrMore = <one> Thing <more> SeveralThings . Thing : NestedBasket | Fruit.

Fruit : Apple | Orange *common* <weight> DemNumber.

Apple = . Orange = .

Thing_List ~ {Thing}.

Find the unknowns below by completing the propagation patterns based on the informal task description at the beginning of each propagation pattern.

This question requires knowledge about propagation patterns returning a value. Such examples have been used in this chapter but a complete treatment is in the chapter on propagation pattern interpretation in the subsection on propagation patterns with return types. In a nutshell, propagation patterns with a return type have a variable return val available having the same type as the return type. This variable is initialized with an *init*

clause.

A nal hint: for repetition classes a function append is available that appends its rst and only argument to the end of the list.

// Add the weight of all Apple-objects.

*operation* int all_apples() *init* (@ UNKNOWN1 @)

*traverse*

*from* UNKNOWN2 *to* UNKNOWN3

*wrapper* UNKNOWN4

*prefix* (@ UNKNOWN5 += *(this -> UNKNOWN6()); @)

5.5. EXERCISES 133

// Add the weight of all Fruit-objects.

*operation* int all_fruit() *init* (@ UNKNOWN7 @)

*traverse*

*from* UNKNOWN8 *to* UNKNOWN9

*wrapper* UNKNOWN10

*prefix* (@ UNKNOWN11 += *(UNKNOWN12()); @) // Produce a list of all Thing-objects.

*operation* Thing_List* all_thing() *init* (@ UNKNOWN13 @)

*traverse*

*from* Basket *to* UNKNOWN14

*wrapper* UNKNOWN15

*prefix* (@ UNKNOWN16 -> UNKNOWN17(this); @)

// Produce a list of all Thing-objects which contain an Apple.

*operation* Thing_List* all_thing_containing_apples(int& apple_count) // is called with variable as first argument which has value 0

*init* (@ new Thing_List() @)

*traverse*

*from* Basket *via* Thing *to* Apple

*wrapper* Apple

*prefix*

(@ apple_count ++ ;@)

*wrapper* Thing

*prefix* (@ int UNKNOWN18 = apple_count; @)

*suffix* (@ if (UNKNOWN19) UNKNOWN20 -> UNKNOWN21(this); @) //Add two to the weight of all Orange-objects.

*operation* void oranges_plus_two()

*traverse*

*from* Basket *to* Orange

*wrapper* Orange

*prefix* (@

cout << "weight before change " << this-> get_weight() << endl;

UNKNOWN22;

cout << "weight after change " << this-> get_weight() << endl;

@)

Exercise 5.2

Give an object example from which you can abstract the following class dictionary graph.

Basket = <contents> SeveralThings.

SeveralThings : None | OneOrMore.

None = .

OneOrMore = <one> Thing <more> SeveralThings.

Thing : Basket | Fruit.

134 CHAPTER5. ADAPTIVESOFTWAREBYEXAMPLE

Fruit : Apple | Orange *common* <weight> DemNumber.

Apple = . Orange = .

5.6 BIBLIOGRAPHIC REMARKS

Incremental and global algorithms for class dictionary graph learning and optimization are in [BL91, LBS90, LBS91, Ber94].

The concept of propagation patterns was developed in a series of publications: [LXS91, LX93c] and applied in [HSX91, LHSX92, Lie92, LSX94].

The rst, preliminary implementation of propagation patterns in the form of a C++

skeleton generator was done by Christos Stamelos in the winter of 1990. The implementation of propagation patterns was brought to its current form primarily through the eorts of Cun Xiao and the feedback from hundreds of professionals who work in the Boston software industry and who took my courses in which adaptive software is used. Linda Keszenheimer, T. Beutel, and Gregory Bratshpis were early users. Paul Steckler was involved in the early development of propagation patterns.

Propagation patterns were motivated by the Law of Demeter. A strong driving force behind propagation patterns was G. Brown since he involved us in a project with initially vague assumptions on the class structure.

5.7 SOLUTIONS