1 advanced abstraction mechanisms mixins traits delegation

26
1 Advanced Abstraction Advanced Abstraction Mechanisms Mechanisms Mixins Traits Delegation

Post on 22-Dec-2015

220 views

Category:

Documents


3 download

TRANSCRIPT

Page 1: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

1

Advanced Abstraction Advanced Abstraction MechanismsMechanisms

Mixins Traits Delegation

Page 2: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

2

Int = Printer + NumberInt = Printer + Numberstruct 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" ?! }

Page 3: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

3

Discussion #1Discussion #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

Page 4: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

4

Int Class, 2Int Class, 2ndnd Attempt Attemptstruct 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(); }

Page 5: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

5

Discussion #2Discussion #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

Page 6: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

633rdrd Attempt: Mazal Tov Attempt: Mazal Tovstruct 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(); }

Page 7: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

7

Discussion #3Discussion #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

Page 8: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

8

MixinsMixins 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

Page 9: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

9

Mixin: C++Mixin: C++struct Number { int n; void setValue(int v) { n = v; } virtual int getValue() { return n; }}; template<typename S> // Define the mixinstruct Printer : S { void print() { cout << getValue() << endl; }}; struct Int : Printer<Number> // Instantiate the mixin{ };

int main(int, char**) { Int* n = new Int(); n->setValue(5); n->print(); }

Page 10: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

10

Properties of MixinsProperties 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

Page 11: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

11

C++: A Mixin is not a TypeC++: A Mixin is not a Type

struct Number { ... }; template<typename S> struct Printer : S { ... } struct Int : Printer<Number> { };

int main(int, char**) { Printer<Number>* pn = new Int(); Printer* p = new Int(); // Compiler error!}

Page 12: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

12

Workaround for the Workaround for the Mixin-is-not-a-type problemMixin-is-not-a-type problem

struct Number { int getValue() { return 10; } };

struct Printable { virtual void print() = 0; };

template<typename S>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<Number> { };

int main(int, char**) { Printable* p = new Int(); p->print();}

Page 13: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

13

Jam: Java with MixinsJam: Java with Mixinspublic 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; }

Page 14: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

14

Discussion: Jam MixinsDiscussion: 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)

Page 15: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

15

Linearization is Not So Great #1Linearization 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 { }

Page 16: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

16

Linearization is Not So Great #2Linearization 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

Page 17: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

17

Traits:Traits:Flattening instead of LinearizationFlattening 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

Page 18: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

18Java with TraitsJava with Traitstrait 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 typet1.add(3);

Page 19: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

19Conflict ResolutionConflict Resolutiontrait 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}

Page 20: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

20

Class Composition vs. Object CompositionClass 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?

Page 21: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

21

Object Composition - MotivationObject Composition - Motivationpackage 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

Page 22: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

22

Object Composition – Manual DelegationObject 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)

Page 23: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

23

Drawbacks of Manual DelegationDrawbacks 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)

Page 24: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

24Overriding is PartialOverriding is Partialinterface 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" :(

Page 25: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

25Lava: ImplementationLava: 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 }}

Page 26: 1 Advanced Abstraction Mechanisms  Mixins  Traits  Delegation

26Delegation is Far From Being PerfectDelegation 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();