Nested Collaborations Collaboration reuse We want to reuse
Nested Collaborations
Collaboration reuse We want to reuse collaborations at two levels: • Refined collaborations – similar to is-a relationship between classes • Nested collaborations – similar to has-a relationship between classes This idea was already in contracts (Helm, Holland et al. ) and in Holland’s thesis.
Boolean Collaborations • Collaboration is trivial: only one participant • Example is realistic because we want to record the result history. • Example indicates that each subcollaboration should be represented by an object. • Only in special cases can collaborations be implemented by code insertion only.
Recording And Collaboration collaboration And { participant D { boolean res; public Vector r = new Vector(); // result history public boolean and. R() { res = input 1() and input 2(); r. append(new Boolean(res); return res; } expect boolean input 1(); expect boolean input 2(); } } Each time and. R() is called, return value will be stored in a vector local to participant D.
Recording Or Collaboration collaboration Or { participant D { boolean res; public Vector r = new Vector(); // result history public boolean or. R() { res = input 1() or input 2(); r. append(new Boolean(res); return res; } expect boolean input 1(); expect boolean input 2(); } } Each time or. R() is called, return value will be stored in a vector local to participant D.
Compose Collaborations collaboration instances And Or And. Instance Or Or. Instance And. Or
collaboration And. Or { participant D { expect boolean input 1(); expect boolean input 2(); expect boolean input 3(); public boolean and. Or. R() {return And. Adapter. D. and. R(); } public void and. Or. Statistics() { System. out. println(And. Adapter. D. r(true); System. out. println(And. Adapter. D. r(false); System. out. println(Or. Adapter. D. r(true); System. out. println(Or. Adapter. D. r(false); } adapter And. Adapter { And. Or. D plays And. D { boolean input 1() {return And. Or. D. input 1(); } boolean input 2() {return Or. Adapter. D. or. R(); }}} adapter Or. Adapter { And. Or. D plays Or. D { boolean input 1() {return And. Or. D. input 2(); } boolean input 2() {return And. Or. D. input 3(); }}} }
collaboration And. Or { with Pengcheng 1 participant D { expect boolean input 1(); expect boolean input 2(); expect boolean input 3(); public boolean and. Or. R() {return And. Instance. D. and. R(); } public void and. Or. Statistics() { System. out. println(And. Instance. D. r(true); System. out. println(And. Instance. D. r(false); System. out. println(Or. Instance. D. r(true); System. out. println(Or. Instance. D. r(false); } adapter for And creating And. Instance { And. Or. D plays And. D { boolean input 1() {return And. Or. D. input 1(); } boolean input 2() {return Or. Instance. D. or. R(); }}} adapter for Or creating Or. Instance { And. Or. D plays Or. D { boolean input 1() {return And. Or. D. input 2(); } boolean input 2() {return And. Or. D. input 3(); }}} }
collaboration And. Or { with Pengcheng 2 participant D { expect boolean input 1(); expect boolean input 2(); expect boolean input 3(); public boolean and. Or. R() { Or. D d = new Or. D(); boolean res 1 = d. or. R(); return (new And. D. and. R(); } public void and. Or. Statistics() { System. out. println(And. Instance. D. r(true); System. out. println(And. Instance. D. r(false); System. out. println(Or. Instance. D. r(true); System. out. println(Or. Instance. D. r(false); } adapter creating And. Instance { And. Or. D plays And. D { boolean input 1() {return And. Or. D. input 1(); } boolean input 2() {return Or. Instance. D. or. R(); }}} adapter creating Or. Instance { And. Or. D plays Or. D { boolean input 1() {return And. Or. D. input 2(); } boolean input 2() {return And. Or. D. input 3(); }}}
Conflict with Result Vector? • No: each adapter has its own
Hardware-style Components • • Can be expressed with nested collaborations Adapters are used to instantiate components It is important to name adapters How should collaborations be implemented? Adapted collaborations should be represented as objects.
Methods versus Signals • The model we uses methods for input. • An alternative model would be to use signals on ports.
Counting Collaboration • Have Counting. Adapter. Bus. Route • Adapter creates adapter object • When count() is invoked, count() on adapter object is invoked? Bus. Route. count() calls Counting. Adapter. Bus. Route. count(). But we might have another adapter. Need to rename count in adapter in that case.
Better example • Pricing and Summing: add Order participant to participant graph of Pricing. • Call it Order. Pricing because it computes the cost of an order not just of a line item. Both negotiated and regular price. • Mapping for Pricing is identity? Mapping for Summing is Source -> Order, Target -> Line. Item.
Line. Item. Pricing example collaboration Line. Item. Pricing { private int qty; private float unit. Price; participant Line. Item { //Order. Unit abstract Product get. Product(); // abstract instead of expect abstract Pricer get. Pricer(); Expected abstract Customer get. Customer(); Interface abstract int get. Quantity(); public float price() { qty = get. Quantity(); prod = get. Product(); k = get. Customer(); unit. Price = get. Pricer(). unit. Price(prod, k); float price = unit. Price + get. Product(). extra. Costs(); return price; Provided Interface } } Line. Item = Product Pricer Customer <quantity> int. Product = List(Extra. Cost). Customer =. Pricer =. Extra. Cost =.
participant Product { abstract Extra. Cost[] get. Extra. Costs(); private float extra. Costs() { float total = 0; while (get. Extra. Costs(). has. Element()) total =+ get. Extra. Costs(). next(). get. Amount(qty, unit. Price, this); return total; } } participant Pricer { abstract float get. Basic. Price(Product prod); abstract float get. Discount(Product prod, Customer k); private float unit. Price(Product prod, Customer k) { float basic. Pr = get. Basic. Price(prod); return basic. Pr - (get. Discount(prod, k) * basic. Pr); } } participant Extra. Cost{ abstract float get. Amount(int qty, float unit. Price, Product prod); } }
Office. Product. basic. Price Fig 1 a: Order. Pricing Price. For. Line. Item Sum. For. Price Order Tax Source Product. Category Summing Line. Item Office. Product price Line. Item Product Pricer to. Sum Target Line. Item. Pricing Customer Extra. Cost sum Order. price
RPrice. For. Line. Item Sum. For. RPrice NPrice. For. Line. Item Sum. For. NPrice Order. Pricing Order Product. Category Line. Item Office. Product Source price Target to. Sum price Line. Item. Pricing Product Pricer Customer rprice sum nprice Source Summing to. Sum sum Summing Target Line. Item. Pricing Product Pricer Customer
Summing Collaboration collaboration Summing { participant Source { expect Traversal. Graph get. T(); public int sum (){ // traversal/visitor weaving get. T(). traverse(this, new Visitor(){ int r; public void before(Target host){ r=r+host. to. Sum(); } public void start() { r = 0; } …) } } participant Target { expect int to. Sum(); } } Base: regular Meta variable: bold Keywords: underscore
Composed collaboration Order. Pricing // Participant graph: first only for negotiated price, followed by regular // price Order = <product. Categories> List(Product. Category) Customer. Product. Category = Name List(Line. Item). Line. Item = Office. Product Pricer Customer <quantity> int. Office. Product = <extra. Costs> List(Tax). Tax =. Customer =. Pricer =. // Customer in Order is transported to Customer in Line. Item
Adapter Sum. For. Price adapter Sum. For. Price { Order is Summing. Source with { Traversal. Graph get. T() { Class. Graph class. Graph 1 = new Class. Graph(); return new Traversal. Graph(class. Graph 1, new Strategy(“from Order to Line. Item”)); } } Line. Item is Summing. Target with int to. Sum{ Neg. Price. For. Order. Line. Item. price(); } }
Adapter Neg. Price. For. Line. Item adapter Neg. Price. For. Line. Item { Line. Item is Line. Item. Pricing. Line. Item Office. Product is Line. Item. Pricing. Product Customer is Line. Item. Pricing. Pricer with { float get. Basic. Price (Product p) { return negotiated. Price(p); } float get. Discount(Product p, Customer c) { return negotiated. Discount(p); }} Tax is Extra. Cost with { … } }
Neg. Order. Pricing Aspect Pattern collaboration Neg. Order. Pricing { participant Order { public int neg. Price (){ Sum. For. Price. Order. sum(); } // participant graph and the adapters Sum. For. Price and // Neg. Price. For. Order seen earlier } Base: regular Meta variable: bold Keywords: underscore
Now add regular price computation • Need a new adapter for summing regular prices: notice need to parameterize adapters • Need a new adapter for computing regular price • Need to distinguish between two price methods: reg. Price and neg. Price.
Adapter Sum. Reg. For. Price adapter Sum. For. Reg. Price { Order is Summing. Source with { Traversal. Graph get. T() { Class. Graph class. Graph 1 = new Class. Graph(); return new Traversal. Graph(class. Graph 1, new Strategy(“from Order to Line. Item”)); } } Line. Item is Summing. Target with int to. Sum{ Reg. Price. For. Line. Item. price(); } }
Adapter Reg. Price. For. Order adapter Reg. Price. For. Order { Line. Item is Line. Item. Pricing. Line. Item Office. Product is Line. Item. Pricing. Product Office. Product is Line. Item. Pricing. Pricer with { float get. Basic. Price (Product p) { return p. reg. Price(); } float get. Discount(Product p, Customer c) { return p. reg. Discount(); }} Tax is Extra. Cost with { … } }
Order. Pricing Aspect Pattern collaboration Order. Pricing { participant Order { public int neg. Price (){ Sum. For. Price. Order. sum(); } // participant graph and the adapters Sum. For. Price and // Neg. Price. For. Order seen earlier public int reg. Price (){ Sum. For. Reg. Price. Order. sum(); } // participant graph and the adapters Sum. Reg. For. Price and // Reg. Price. For. Order seen earlier } Base: regular Meta variable: bold Keywords: underscore
connector Negotiated. Price { Bestellung is Line. Item. Party; Kunde is Pricer with { float get. Basic. Price (Product p) { return verhandelter. Preis (p); } float get. Discount(Product p, Customer c) { return verhandelter. Discount (p); } } Büromaterial is Product with { Extra. Cost[] extra. Costs () {. . . }; } Steuer is Zusatzkosten with { float get. Amount (. . . ) {. . . }; } } Role of Pricer is once played by Kunde and once by Büromaterial connector Regular. Price { Bestellung is Line. Item. Party; Büromaterial is Pricer with { float get. Basic. Price(Product p) { return p. reg. Preis(); } float get. Discount(Product p, Customer c) { return p. reg. Discount(c); } }; Büromaterial is Product with { Extra. Cost[] extra. Costs() {. . . }; } Steuer is Extra. Cost with { float get. Amount (. . . ) {. . . }; } }
Need for collaboration composition • Need two instances of the summing collaboration. • Need to instances of the line item cost collaboration.
Collaborations • Collaboration – Participant graph: for each participant a provided and required interface – Set C of reused collaboration names and the corresponding adapters
Tim Sheard • Mark meta variables • express meta code • show base code can become meta code
Example : Count Aspect Pattern aspect pattern Counting { participant Source { expect Traversal. Graph get. T(); public int count (){ // traversal/visitor weaving get. T(). traverse(this, new Visitor(){ int r; public void before(Target host){ r++; } public void start() { r = 0; } …) } } participant Target {} } Base: Meta variable: bold Keywords: underscore
Adapter 1 class. Graph 1 is fixed and therefore the traversal is fixed adapter Counting. For. Bus. Route 1 { Bus. Route is Counting. Source with { Traversal. Graph get. T() { Class. Graph class. Graph 1 = new Class. Graph(); return new Traversal. Graph(class. Graph 1, new Strategy(“from Bus. Route via Bus. Stop to Person”)); } } Person is Counting. Target { } }
Aspect Pattern Partial Evaluation • Because the class graph is fixed for Counting. For. Bus. Route 1, it can be frozen and a more efficient specialization can be produced. aspect specialization Counting. For. Bus. Route 1 freeze get. T() in get. T(). traverse(…) Visitor constant too!
Aspect pattern example aspect pattern Pricing { private int qty; private float unit. Price; participant Line. Item. Party { //Order. Unit abstract Product get. Product(); // abstract instead of expect abstract Pricer get. Pricer(); Expected abstract Customer get. Customer(); Interface abstract int get. Quantity(); public float price() { Provided Interface qty = get. Quantity(); prod = get. Product(); k = get. Customer(); unit. Price = get. Pricer(). unit. Price(prod, k); float price = unit. Price + get. Product(). extra. Costs(); return price; } } Line. Item. Party = Product Pricer Customer <quantity> int. Product = List(Extra. Cost). Customer =. ? Pricer =.
participant Product { abstract Extra. Cost[] get. Extra. Costs(); private float extra. Costs() { float total = 0; while (get. Extra. Costs(). has. Element()) total =+ get. Extra. Costs(). next(). get. Amount(qty, unit. Price, this); return total; } } participant Pricer { abstract float get. Basic. Price(Product prod); abstract float get. Discount(Product prod, Customer k); private float unit. Price(Product prod, Customer k) { float basic. Pr = get. Basic. Price(prod); return basic. Pr - (get. Discount(prod, k) * basic. Pr); } } participant Extra. Cost{ abstract float get. Amount(int qty, float unit. Price, Product prod); } }
Aspect Patterns: Adapters adapter Regular. Price { Office material vendor Bestellung is Line. Item. Party; Büromaterial is Pricer with { float get. Basic. Price(Product p) { return p. reg. Preis(); } float get. Discount(Product p, Customer c) { return p. reg. Discount(c); } }; Büromaterial is Product with { Extra. Cost[] extra. Costs() {. . . }; } Steuer is Extra. Cost with { float get. Amount (. . . ) {. . . }; } } Price computation
adapter Negotiated. Price { Bestellung is Line. Item. Party; Kunde is Pricer with { float get. Basic. Price (Product p) { return verhandelter. Preis (p); } float get. Discount(Product p, Customer c) { return verhandelter. Discount (p); } } Büromaterial is Product with { Extra. Cost[] extra. Costs () {. . . }; } Steuer is Zusatzkosten with { float get. Amount (. . . ) {. . . }; } } Role of Pricer is once played by Kunde and once by Büromaterial adapter Regular. Price { Bestellung is Line. Item. Party; Büromaterial is Pricer with { float get. Basic. Price(Product p) { return p. reg. Preis(); } float get. Discount(Product p, Customer c) { return p. reg. Discount(c); } }; Büromaterial is Product with { Extra. Cost[] extra. Costs() {. . . }; } Steuer is Extra. Cost with { float get. Amount (. . . ) {. . . }; } }
adapter Sales. Price { Bestellung is Line. Item. Party; Büromaterial is Pricer with { float get. Basic. Price(Product p) { return p. sales. Price(); } float get. Discount(Product p, Customer c) {return p. sales. Discount(c); } }; Büromaterial is Product with { Extra. Cost[] extra. Costs() {. . . }; } Steuer is Extra. Cost with { float get. Amount (. . . ) {. . . }; } }
Example: Feature-oriented Programming • Dependent aspects • Order of deployment is relevant
Data. With. Counter : pairwise interaction Data/Counter aspect pattern Data. With. Counter { private participant Counter { int i=0; void reset(){i=0; }; void inc(){…}; void dec(){…}; } participant Data. Structure { protected Counter counter; expect void init. Counter(); expect void make_empty(); expect void push(Object a); expect void pop(); replace void make_empty(){counter. reset(); expected(); } replace void push(Object a){counter. inc(); expected(a); } replace void pop() {counter. dec(); expected(); } } }
Data. With. Counter : pairwise interaction Data/Counter make_empty push pop Data. With. Counter make_empty push pop
Data. With. Lock Pattern pairwise interaction Data/Lock aspect pattern Data. With. Lock { participant Data { Lock lock; expect void init. Lock(); expect Any. Type method_to_wrap(Object[] args); replace Any. Type method_to_wrap(Object[] args) { if (lock. is_unlocked()) { lock(); expected(Object[] args); lock. unlock(); }}} private participant Lock {boolean l = true; void lock(){…}; void unlock(){…}; boolean is_unlocked(){return l}; }
Data. With. Lock Pattern pairwise interaction Data/Lock method_to_wrap Data. With. Lock method_to_wrap
Stack. Impl Queue. Impl Data. With. Counter Data. With. Lock
First adapter add. Counter. And. Lock { Stack. Impl is Data. With. Counter. Data. Structure with { void init. Counter() {counter = new Counter(); } void push(Object obj) {push(obj)); } // use name map instead Object top() {return top(); }. . . } Stack. Impl is Data. With. Lock. Data with { method_to_wrap = {pop, push, top, make_empty, init. Counter}; }; Queue. Impl is Data. With. Counter. Data. Structure with {. . . } is Data. With. Lock. Data with {. . . }; }
Data. With. Counter Data. With. Lock Data. With. Counter. And. Lock
Data. With. Counter : pairwise interaction Data/Counter make_empty push pop Data. With. Counter make_empty push pop
Data. With. Lock Pattern pairwise interaction Data/Lock method_to_wrap Data. With. Lock method_to_wrap
Data. With. Counter. And. Lock pattern composition make_empty push Data. With. Counter pop make_empty’ push’ pop’ to-wrap Data. With. Lock make_empty’’ push’’ pop’’ Name stays the same, but behavior changes Should composed component have its interface computed? Yes?
Method flow diagram • • • Composition of aspect patterns defines method flow Methods come in and out of patterns Patterns may create new methods and absorb methods Graph: nodes are pattern, edges are methods What about participant graph? Participant name part of method name. • Should nodes have ports? • Multiple edges in, multiple edges out
Create composed aspects prior to deployment aspect pattern Data. With. Counter. And. Lock uses Data. With. Counter, Data. With. Lock { participant Data { expect void make_empty(); expect void push(Object a); expect void pop(); } adapter Data. With. Counter. A { Data is Data. With. Counter. Data. Structure} // identity name map adapter Data. With. Lock. A { Data plays Data. With. Lock. Data with { method-to-wrap = replaced methods of Data. With. Counter. A } } } // ordering of adapters is relevant
Create composed aspects prior to deployment Do we need this nested adapter syntax? aspect pattern Data. With. Counter. And. Lock { participant Data = Data. With. Counter. Data. Structure is Data. With. Lock. Data with { method-to-wrap = {make_empty, pop, push}}; }
Create composed aspects prior to deployment aspect pattern Data. With. Counter. And. Lock { participant Data = Data. With. Counter. Data. Structure is Data. With. Lock. Data with { method-to-wrap = {make_empty, pop, top, push}}; } Instantiate Data. With. Counter and Data. With. Lock
Second connector: Deploy composed component connector add. Counter&Lock { Stack. Impl is Data. With. Counter. And. Lock. Data with { void make_empty() {empty(); } void init. Counter() { counter = new Counter(); } void push(Object obj) {push(obj); }. . . }; Queue. Impl is Data. With. Counter. And. Lock. Data with {. . . }; }
Doug Schmidt • Apply collaborations and adapters to express middleware: Namingserver, Event. Channel • Express basic patterns, compose them to core functionality. Add synchronization, persistence, and later real-time aspects. Compare handwritten code with generated code.
- Slides: 56