430 likes | 544 Views
Object reclassification and Evolution. Object Evolution - Tal Cohen, Yossi Gil Fickle (ECOOP 2001) Sophia Drossopoulou, Ferruccio Damiani, Mariangiola Dezani-Ciancaglini, Paola Giannini. Presented by: Oren D. Rubin. Agenda. Motivation – current trends Multiple inheritance Mixin
E N D
Object reclassification and Evolution Object Evolution - Tal Cohen, Yossi Gil Fickle (ECOOP 2001) Sophia Drossopoulou, Ferruccio Damiani, Mariangiola Dezani-Ciancaglini, Paola Giannini Presented by: Oren D. Rubin
Agenda • Motivation – current trends • Multiple inheritance • Mixin • Object Evolution • Inheritance • Mixin • Shakeins • Fickle
Motivation The quest for better reuse mechanisms • Common forms of inheritance • Single • Multiple • ‘Mixin’
Sync these classes NOW!! Motivation The challenge: Your manager asks you to extend two classes (classes A and B), which both have some read\write Operations. He wants you to code new classes which support same operations but these operations are now synchronized using a lock (classes SyncA and SyncB).
class SyncReadWrite { public: virtual int read() { acquireLock(); result = directRead(); releaseLock(); return result; } virtual void write(int n) { acquireLock(); directWrite(n); releaseLock(); } protected: virtual void acquireLock() { // acquire lock } virtual void releaseLock() { // release lock } virtual int directRead() = 0; virtual void directWrite(int n) = 0; }; // end of class SyncReadWrite Solution 1: Use C++’s multiple inheritance, base class with abstract methods
class SyncA : public A, SyncReadWrite { public: virtual int read() { return SyncReadWrite::read(); } virtual void write(int n) { SyncReadWrite::write(n); } protected: virtual int directRead() { return A::read(); } virtual void directWrite(int n) { A::write(n); } }; // end of class SyncA class SyncB : public B, SyncReadWrite { public: virtual int read() { return SyncReadWrite::read(); } virtual void write(int n) { SyncReadWrite::write(n); } protected: virtual int directRead() { return B::read(); } virtual void directWrite(int n) { B::write(n); } }; // end of class SyncB Solution 1: Use C++’s multiple inheritance, base class with abstract methods Code duplication
Let’s mix it! • A mixin is an (abstract subclass) specification • may be applied to various classes to extend them with the same set of features • Key idea: create components designed for reuse rather than for instantiation • Could be layered one on top of the another • A* a = new Cache< Sync<A> >; • Is Cache<Sync<A>> same as Sync<Cache<A>>?!?
template <class Super> classSync : public Super { public: virtual int read() { acquireLock(); result = Super::read(); releaseLock(); return result; } virtual void write(int n) { acquireLock(); Super::write(n); releaseLock(); } protected: virtual void acquireLock() { // acquire lock } virtual void releaseLock() { // release lock } }; // end of MSyncReadWrite // used either A* a = new Sync<A>(); B* b = new Sync<B>(); // or class SyncA : public Sync<A> {}; class SyncB : public Sync<B> {}; Solution 1: Use C++’s Mixins (no need for multiple inheritance)
Object Evolution Tal Cohen Dr. Yossi Gil • Reclassification (evolution) changes at run-time the class membership of an object while retaining its identity.
I-Evolution • Based on standard inheritance • Evolution is irreversible • Instance of a class can ‘evolve’ to subclasses • Example: V v = new V(); V v2 = v; //Transparent to aliasing . vC(…); // evolves object referenced by variable v C must be a subclass of v’s type
I-Evolution-Implementing The State Design Pattern TCPConnection _______________________ TCPState state _______________________ Open() {state.Open} Close() {state.Close} Acknowledge(){…} TCPState _______________________ Open() Close() Acknowledge() TCPEsbablished ____________ Open() Close() Acknowledge() TCPListen ____________ Open() Close() Acknowledge() TCPClosed ____________ Open() Close() Acknowledge()
I-Evolution With Object Evolution: • Only the 3 state (concrete) classes are need – no need for abstract state class and the wrapper • Much more elegant (and shorter) TCPClosed.open(){ // open connection thisTCPListen(); } • More efficient, no need to delegate messages any more
I-Evolution - Lazy Data Structure • Lazy Data Structure <html> <head> <title> My document <title> </head> <body> <h1> Header </h1> <p> Paragraph </p> </body> </html>
I-Evolution-Implementing DOM trees Node _______________________ appendChild() getChildNodes() Text _______________________ getText() setText() Element _______________________ getName() getAttribute() Document ____________________ …. Head ____________________ …. Body ____________________ …. Paragraph ____________________ ….
I-Evolution-Implementing DOM trees • Nodes in the DOM tree are only created when they are accessed • The initial call to getDocument() will return a DOM tree that consists only of the Document node • All other nodes exist unexpanded (virtual) • All of the immediate children of a Node are created when any of that Node's children are accessed • A node can even exist without its parent being expanded (for some implementations) • This shortens the time it takes to parse an XML file and create a DOM tree at the expense of requiring more memory during parsing and traversing the document
I-Evolution Node n = new Node(); Node alias = n; nHead (); n.getAttributes(); The evolution is done on the object, not the reference (variable) n’s static type is now Head until end of block
I-Evolution - exam Node n = new Node(..); Node alias = n; If( someCondition ){ n.getAttributes(); nHead(); n.getAttributes(); alias.getAttributes(); (Head)alias.getAttributes(); } n.getAttributes(); (Head)alias.getAttributes();
I-Evolution Node n = new Node(..); Node alias = n; If( someCondition ){ n.getAttributes(); // Compile time error nHead(); n.getAttributes(); // success alias.getAttributes(); // Compile time error (Head)alias.getAttributes(); } n.getAttributes(); // Compile time error (Head)alias.getAttributes(); // Downcast might fail at runtime
I-Evolution - Evolvers • Constructor-like • Syntax C(…) • No return type • Default Evolvers • Monotonic • Declare throws • Not constructor-like • no need for super, why?
I-Evolution - When this evolves public class B { public void foo() { println(“B.foo”); } public void bar(){ println(“B.bar”); } private void baz(){ println(“B.baz”); } { public class A { public void foo() { println(“A.foo – before”); thisB(); println(“A.foo – after”); bar(); baz(); } public void bar(){ println(“A.bar”); } private void baz(){ println(“A.baz”); } { Console A.foo – before A.foo – after B.bar A.baz
I-Evolution –Evolution Failures • NullPointerException - when vC() • v is null • RunTimeException – when vC() • v’s dynamic type is not a subtype of C • The Evolver throws an exception • Direct or indirect • What happened is irreversible Recall class Head which extended Entity which extended Node Node n = new Node(); Try{ nHead(); // Entity’s Evolver succeeds, Head’s Evolver throws exception } Catch{ n.doSomething(); // object referenced by n is now of type Entity }
M-Evolution - Mixin • To make the next code possible List myList = new ArrayList(); myList.add(..) // add numerous data items myList->Blocked<Vector>(); • We need Mixin Blocked { inherited public void add(Object o); inherited public void remove(int index); public final void add(Object o){ throw new IllegalOperationException(); } public final void remove(int index){ throw new IllegalOperationException(); } }
M-Evolution • Very similar to I-Evolution • Same as vM<V>() • If v’s dynamic type is V • But no need for Public void blockParam(List list){ if (list instanceOf ArrayList) listBlocked<ArrayList>(); else if (list instanceOf Vector) listBlocked<Vector>(); else throw new RunTimeException(“unknown list implementation”); }
M-Evolution • Solution – The Mixin receives a variable as parameter, not a Type listBlocked<lst>(); • No evolution dead-end • Never fails?!
M-Evolution - Idempotent • Example of an Idempotent @Idempotent public mixin Undo{ inherited public String getText(); inherited public void setText(String s); private String lastString; public void setText(String s){ lastText = getText(); super.setText(s); } public void undo(){ setText(lastText); } }
M-Evolution • Idempotent • Generates M<T> although asked for M< M<T> > • Mixin is idempotent if • It is annotated using @Idempotet • Meets two criteria • Introduces no new members (except for private) • Any overridden method replaces (doesn’t refine) its inherited version
M-Evolution - Mixin failures • Two esoteric cases • Failure by final declarations of a class • class is declared as final • Accidental overriding • Mixin attempts to introduce new method, only to find a method with this signature already exists public class UndoableJButton extends JButton { public void undo(){ // does something }
S-Evolution – Evolving with Shakeins • Shakeins – Programming construct that generates a new class from a given class parameter • As opposite to mixin • Generates new class, not new type • Can use pointcut expressions and advice to selectiverly generate new implementations of methods in the original class
S-Evolution – Shakein Example @Idempotent public shakein Transactional{ pointcut publicMethod:= public *(*); around: publicMethod { Transaction tx = new Session.getTransaction(); try{ tx.begin(); result = proceed(); // invoke original method tx.commit(); } catch(Exception e){ tx.rollback(); } } }
S-Evolution – example of class hierarchy C2, C3 extends C1 C4 extends C2 S’ and S’’ are implemented using S Type C1 S’<C1> C1 S<C1> S<C4> is subtype of S’<C2> S’’<C1> Type C2 Type C3 S’<C2> S’<C3> C2 S<C2> C3 S<C3> S’’<C2> S’’<C3> Type C4 S’<C4> C4 S<C4> S’’<C4>
S-Evolution – Shakein State-Group • Set of Shakeins that share the @StateGroup annotation (with the same String parameter) • Shakeins in the same state-group are mutually exclusive for arbitrary class C, S1 and S2 are in the same state-group • Applying S2 on S1<C> results in S2<C> • Overcomes the inability of the I-Evolution-based solution to retract itself • Transitions within a state-group are type safe • The compiler enforces the limitation that all shakeins in a given state-group define the same set of private class members • State-group are only for shakeins and not for mixins • Established e = new Established<TCPConnection>(); • Some message could change its state from Established to Closed • Perfect for our ‘TCPConnection classes’…
S-Evolution @StateGroup(“Connection”) public shakein Listen { public void open(){… thisEstablished<this>(); } public void close(){… thisClose<this>(); } public void acknowledge(){…} } @StateGroup(“Connection”) public shakein Established { public void open(){ /* ignore*/ } public void close(){… thisClose<this>(); } public void acknowledge(){…} } @StateGroup(“Connection”) public shakein Closed { public void open(){ throw new IllegalStateException(); } public void close() { throw new IllegalStateException(); } public void acknowledge(){…} }
S-Evolution–shakeins as dynamic aspects Class definition @stateGroup(“Log”) public shakein Log[String filename] { pointcut publicMethod = public (*); before: publicMethod { FileOutputStream fos = new FileOutputStream(filename); // log the operation to the file proceed(); } } @stateGroup(“Log”) public shakein NoLog { // No changes to the class } Usage LstLog[system.log]<lst>(); LstNoLog<lst>();
S-Evolution • Only fails on final classes • Can be marked as idempotent • No accidental override • Classes can have multiple states
S-Evolution – Implementation • JVMmust generate class at run-time • E.g. J2EE application servers use this • Object’s behavior is changed at run-time • mold is unchanged – change the VMT • mold is changed (several options) • Run over all references • Use Object handler – using full garbage collection
Fickle • Frogs and princes play. When woken up, frogs blow their pouches and princes swing their swords. class Player { int age; void wake() {} void kiss() {} } root {Prince} class Frog extends Player { Vocal pouch; void wake() { pouch.blow(); } } class Prince extends Player{ Weapon sword; void wake() { sword.swing(); } } state state void kiss(){Prince}{ this⇓Prince }
Fickle Reclassification is transparent to aliasing Player p1, p2; p1 := new Frog; p2 := p1; p2.wake(); // blow pouch p1.kiss(); // reclassify both p2.wake(); // swing sword
Fickle Variables are only root classes: Player charming; Frog kermit; // illegal Kermit = new Frog(); Charming = kermit charming.kiss() // turns to prince Kermit.vocal // field doesn’t exist error
Fickle • More general than evolution • Allow non-monotonic reclassification • Only instances of root can reclassify • Programmer must track the effect of each method • Compiler verifies • Changes are also to the static type • Methods might disappear
Fickle II int play(Frog f1, Frog f2) { f1.pouch; // O.K. f2⇓Prince; // may change also f1 f1.pouch; // could cause ERROR f1.age; // O.K. } Type of this and variables may change through aliasing Frog f; f:= new Frog; play(f,f);
Fickle III - MT • No more root and state classes • Re-classification are traced also by effects • Methods declare pairs of effects • E.g. myMethod {Prince⇓Frog, …} • Fickle-MT added multithreading