<< Dependent Types | Contents | Pointcuts and Advice >>
Bindings
Def: Binding is a relationship between two classes, where one class is declared as
wrapper class and another as
wrappee class. Wrapper serves as a dynamic extension to wrappee to introduce a new state to it or to adapt it to desired interface at run-time.
Binding Declaration (bind.rel)
(bind.rel.1) Binding is declared by
wraps
clause of a Caesar class. The declaring class becomes the wrapper and the class in the clause becomes wrappee.
(bind.rel.2) Only nested Caesar classes can be declared as wrappers.
(bind.rel.3) Wrappee class can be any Java class or a dependent type.
(bind.rel.4) Bindings are inherited by all subclasses of a class. So all subclasses of a wrapper class are also wrapper classes.
(bind.rel.5) Bindings cannot be overridden in subclasses
(bind.rel.6) A class cannot inherit bindings from two different mixins.
Example: List.1 defines a data model for simple electrical schemes. We want to adapt it to a generic
Graph
collaboration defined in List. 2 in order to run algorithms defined for generic graph. Therefore we define a specialization of graph
ElectricGraph
and establish bindings between
Edge
and
Wire
as well as between
Node
and
Component
. So
Edge
class in
ElectricGraph
is wrapper of
Wire
and
Wire
is wrappee of
Edge
.
Listing 1.
public class Wire {
public Pin getStartPin() { ... }
public Pin getEndPin() { ... }
...
}
public class Pin {
public Component getOwner() { ... }
public Iterator getAttachedWires() { ... }
...
}
public class Component {
public int getPinCount() { ... }
public Pin getPinByNr(int nr) { ... }
...
}
Listing 2.
public cclass Graph {
public cclass Edge {
public Node getStartNode { return null; }
public Node getEndNode { return null; }
}
public cclass Node {
public Iterator getEdges() { return null; }
}
}
cclass ElectricGraph extends Graph {
cclass Edge wraps Wire {
...
}
cclass Node wraps Component {
...
}
}
Wrappee Access (bind.wrappee)
(bind.wrappee.1) Each instance of a wrapper class contains a non-null reference to the wrappee object, which is immutable.
(bind.wrappee.2) Wrappee object can be accessed through
wrappee
keyword.
(bind.wrappee.3) The type of the wrappee object of a wrapper can be any subtype of the wrappee class of the wrapper.
(bind.wrappee.4) Wrapper can access only public class members of the wrappee object.
Example: The classes of
ElectricGraph
of List.2. must implement the methods of
Edge
and
Node
by using corresponding methods of
Wire
and
Component
. List.3 shows implementation of
Edge.getStartNode()
. The method uses keyword
wrappee
to access the corresponding instance of
Wire
and call its methods.
Listing 3.
cclass ElectricGraph {
cclass Edge wraps Wire {
public Node getStartNode {
return Node(wrappee.getPin().getOwner());
}
...
}
...
}
Wrapper Creation (bind.wrapper)
(bind.wrapper.1) Wrapper classes cannot be instantiated in a normal way using new operator:
- Instantiation of a virtual class, which statically is known to be a wrapper class is not allowed by compiler
- Polymorphic instantiation of a wrapper class, which cannot be detected by the compiler causes runtime exception.
(bind.wrapper.2) Wrapper classes are instantiated by expression of form
exp.Cls(obj)
, where
-
exp
is an expression returning collaboration containing wrapper class Cls
,
-
obj
is an object, which statically known type is a subtype of the wrappee class declared for Cls
.
The expression has following semantics:
- (bind.wrapper.3) The expression returns wrapper object of class
Cls
of the collaboration instance, returned by exp
.
- (bind.wrapper.4) The returned wrapper object has wrappee reference to object
obj
.
- (bind.wrapper.5) The expression returns always the same wrapper object for the same triple of
-
exp
instance
-
obj
instance
- class
Cls
- (bind.wrapper.6) If the wrapper object for the given triple does not exist, a new wrapper object is created.
(bind.wrapper.7) Expression of form
exp.getCls(o)
returns corresponding wrapper object if it exists, or null if it does not exist.
Example: In List.3
Edge.getStartNode()
calls
Node(wrappee.getPin().getOwner())
to retrieve the wrapper of the component, to which the wire is attached.
Node(..)
is implicitly assumed as
this.Node(..)
.
When
this.Node(..)
is called first time for a certain instance of
Component
, a new
Node
instance is created and related with that component. The later calls of
this.Node(..)
on the same component will always return the same node object.
Wrapper Lifecycle (bind.life)
(bind.life.1) As long as wrapper is referenced from other objects, than its wrappee and its collaboration, it must exist. Then its wrappee and collaboration must exist too.
(bind.life.2) If wrapper is not referenced from other objects, its existence depends on the existence of both its wrappee and its collaboration. When one of these two objects is not more referenced, wrapper must be destroyed, because it is not reachable.
<< Dependent Types | Contents | Pointcuts and Advice >>