Presentation is loading. Please wait.

Presentation is loading. Please wait.

1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation.

Similar presentations


Presentation on theme: "1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation."— Presentation transcript:

1 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

2 2 Int = Printer + Number struct Number { int n; void setValue(int v) { n = v; } virtual int getValue() { return n; } }; struct Printer { virtual int getValue() { return 0; } void print() { cout << getValue() << endl; } }; struct Int : Printer, Number { }; int main(int, char**) { Int* n = new Int(); n->setValue(5); n->print(); // Output: "0" ?! }

3 3 Discussion #1 Printer is not a subclass of Number Number::getValue() does not override Printer::getValue() We want Number::getValue() to win But, when we look at the code we see that the two methods are symmetric

4 4 Int Class, 2 nd Attempt struct Number { int n; void setValue(int v) { n = v; } virtual int getValue() { return n; } }; struct Printer { virtual int getValue() = 0; // Pure virtual function void print() { cout << getValue() << endl; } }; struct Int : Printer, Number { }; int main(int, char**) { Int* n = new Int(); // “Cannot allocate an object of type Int” n->setValue(5); n->print(); }

5 5 Discussion #2 We tried to break the symmetry Printer::getValue() is now abstract (Pure-virtual in C++ jargon) This does not solve the problem The only implementation of getValue() is in Number Which is not a sub-class of Printer => There's no definition that overrides Printer::getValue()‏ => in class Int, Printer::getValue() is unimplemented => class Int is abstract Cannot be instantiated

6 6 3 rd Attempt: Mazal Tov struct Number { int n; void setValue(int v) { n = v; } virtual int getValue() { return n; } }; struct Printer { virtual int getValue() = 0; void print() { cout << getValue() << endl; } }; struct Int : Printer, Number { int getValue() { return Number::getValue(); } }; int main(int, char**) { Int* n = new Int(); n->setValue(5); n->print(); }

7 7 Discussion #3 Pros: We have a winner! Cons: We had to do extra work to “glue” the two classes We have to redo it for every similar method Summary: The problem occurs when.... We have two classes that we want to inherit from One of the superclasses depends on something that is provided by the other Summary: The problem does not occur when.... We have two classes that we want to inherit from No dependency between the superclasses

8 8Mixins A subclassing mechanism Based on the copying principle In normal inheritance we specify two things: S: Superclass D: Definition to copy == Delta to add to S. Mixins: two basic operations A mixin definition: specify D Also specify expectations from S Instantiation: Combine a delta D with a super class S Results in a new class that extends S with the D Result: Define D once, compose it many times Each time with a different S

9 9 Mixin: C++ struct Number { int n; void setValue(int v) { n = v; } virtual int getValue() { return n; } }; template // Define the mixin struct Printer : S { void print() { cout << getValue() << endl; } }; struct Int : Printer // Instantiate the mixin { }; int main(int, char**) { Int* n = new Int(); n->setValue(5); n->print(); }

10 10 Properties of Mixins Separate the D from the S That's why we have two operations: definition & instantiation The same D can be composed many times Each time with a different S Linearization: We have a clear winner The resulting class inherits from D which inherits from S No extra work for gluing the two classes Expectations of D Expressed as calls to non-existing methods Drawbacks of the mixin idiom we just saw (C++): The (uninstantiated) mixin class is not a type We cannot up cast to D D is compiled only when instantiated

11 11 C++: A Mixin is not a Type struct Number {... }; template struct Printer : S {... } struct Int : Printer { }; int main(int, char**) { Printer * pn = new Int(); Printer* p = new Int(); // Compiler error! }

12 12 Workaround for the Mixin-is-not-a-type problem struct Number { int getValue() { return 10; } }; struct Printable { virtual void print() = 0; }; template struct Printer : S, Printable // Inherit from S, but also from Printable // Thus we can use Printable as a type { void print() { cout << getValue() << endl; } }; struct Int : Printer { }; int main(int, char**) { Printable* p = new Int(); p->print(); }

13 13 Jam: Java with Mixins public class Number { private int n; void setValue(int v) { n = v; } int getValue() { return n; } }; mixin Printer { inherited public int getValue(); public void print() { System.out.println(getValue()); } class Int = Printer extends Number { } public void f() { Int n = new Int(); n.setValue(5); Printer p = n; }

14 14 Discussion: Jam Mixins All properties of C++ mixins Separate the D from the S Linearization => Clear winner Expectations are declared explicitly Using the inherited keyword Drawbacks are solved: The (uninstantiated) mixin class is a type D is compiled when defined (becomes an interface)‏

15 15 Linearization is Not So Great #1 Total ordering C extends B extends A We want C to offer B.f() and A.g() ?! class A { public int f() { return 0; } public int g() { return 1; } } mixin B { public int f() { return 2; } public int g() { return 3; } } class C = B extends A { }

16 16 Linearization is Not So Great #2 Fragile inheritance: Adding a method to a mixin (f')‏ May silently override an inherited method (f)‏ The resulting class prefers the behaviour of f

17 17 Traits: Flattening instead of Linearization Trait = A composable unit of behaviour No fields Provides some methods (with behaviour) Requires other methods (abstract) Serves as a type When composing traits, if a method has more than one implementation it becomes abstract

18 18 Java with Traits trait T1 { abstract void add(int v); void inc() { add(1); } } trait T2 { abstract int getValue(); abstract void setValue(int v); void add(int v) { setValue(getValue() + v); } } class Int uses T1, T2 { int n; int getValue() { return n; } void setValue(int v) { n = v; } } T1 t1 = new Int(); // A trait is also type t1.add(3);

19 19 Conflict Resolution trait T1 { void add(int v) { while(--v >= 0) inc(); } void inc() { add(1); } } trait T2 { abstract int getValue(); abstract void setValue(int v); void add(int v) { setValue(getValue() + v); } } class Int uses T1, T2 { // Int can also extend a “normal” superclass int n; int getValue() { return n; } void setValue(int v) { n = v; } void add() { T2.this.add(); } // Resolve the conflict // Otherwise, a compiler error // when compiling Int }

20 20 Class Composition vs. Object Composition So far we saw mechanisms for creating new classes from existing ones AKA: Class composition Q: Can we extend existing objects? E.g., add new methods, fields to an object? A: In Dynamically typed languages - Yes Especially in Javascript, Ruby In LST – partially What about statically typed languages?

21 21 Object Composition - Motivation package java.awt; public class Color { public final Color RED = new Color(255,0,0); public final Color GREEN = new Color(0,255,0); public Color(int r, int g, int b) {... } public int getRed() {... }... } public class MyColor extends Color { public boolean isRedder(Color c) { return getRed() > c.getRed(); } // We want to evaluate: Color.RED.isRedder(Color.GREEN) ‏ // But, we can't because we cannot change the initialization of Color.RED ‏

22 22 Object Composition – Manual Delegation public class MyColor extends Color { private Color inner; public MyColor(Color inner) { this.inner = inner; } public int getRed() { return inner.getRed(); } public int getGreen() { return inner.getGreen(); } public String toString() { return "MyColor: " + inner.toString(); }... public boolean isRedder(Color c) { return getRed() > c.getRed(); } MyColor mc = new MyColor(Color.RED); mc.isRedder(Color.GREEN)‏

23 23 Drawbacks of Manual Delegation A lot of typing Must redefine every method of the inner object Silent errors: If a new method is added We must remember to add a forwarding implementation at the outer class We have two sets of fields In the outer and the inner object But we only use the inner one The “overriding” semantics is only from the outside If the inner object sends a message to itself the inner method is dispatched (Some of these problems are solved if the inner class is an interface)‏

24 24 Overriding is Partial interface Value { int get(); void print(); } class ValueImpl implements Value { int get() { return 5; } void print() { System.out.println(get()); } } class Outer implements Value { private Value inner; Outer(Value inner) { this.inner = inner; } int get() { return 10; } void print() { inner.print(); } } Value v = new Outer(new ValueImpl()): v.get(); // Result: 10 :) ‏ v.print(); // Output: "5" :(

25 25 Lava: Implementation class Outer { private delegation Value inner; Outer(Value v) { inner = v; } int get() { return 10; } // Compiler generated code: void print() { inner.print() // But pass this (instead of inner ) as // the this parameter of the invoked method }

26 26 Delegation is Far From Being Perfect interface I { void f(); void g(); } class A implements I { void f() { g(); h(); } void g() { } void h() { } } class B implement I { void f() { } void g() { } } class C { private delegation I inner = new A(); void g() { inner = new B(); } } new C().f();


Download ppt "1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation."

Similar presentations


Ads by Google