<< Dependent Types | Contents | Using Caesar >>
Composing Collaborations
Propagating Mixin Composition
In the previous chapter we showed how virtual classes can be used to introduce new functionality to class collaborations. We defined
ExprFormat as extension of
ExprModel to implement expression formatting functionality. In a similar way we can implement other functionality on expressions, for example expression evaluation:
public cclass ExprEval extends ExprModel {
abstract public cclass Expr {
abstract public int eval();
}
public cclass Constant {
public int eval() {
return _val;
}
}
public cclass Add {
public int eval() {
return _left.eval() + _right.eval();
}
}
public cclass Mult {
public int eval() {
return _left.eval() * _right.eval();
}
}
}
Modularization of different functionality on expressions helps to evolve these features independently and to introduce new features. We also reduce overall complexity, because we separate different concerns and minimize dependencies between them.
However all these benefits were of little value if there were no way to use these features together on the same expression model. In our case we can have a problem, because cannot format expressions of
ExprEval collaboration and evaluate expressions of
ExprFormat:
public void test() {
final ExprEval eval = new ExprEval();
eval.Expr const1 = eval.new Constant(8);
System.out.println(const1.format()); /* error! */
final ExprFormat fmt = new ExprFormat();
fmt.Expr const2 = fmt.new Constant(5);
int val = const2.eval(); /* error! */
}
Of course, we could have defined
ExprEval as extension of
ExprFormat or the other way around, but it were a substandard solution, which would introduce unnecessary coupling between
ExprEval and
ExprFormat. A better way to solve the problem is to apply mixin composition on these two extensions and in this way generate a collaboration, which has both the formatting and evaluation functionality:
public cclass Expressions extends ExprEval & ExprFormat {
}
public void test() {
final Expressions model = new Expressions();
model.Expr const1 = model.new Constant(8);
System.out.println(const1.format()); /* ok! */
int val = const1.eval(); /* ok! */
}
As we see mixin composition can be applied not only to individual classes but also to collaborations. Mixin composition has propagating effect. It means that when two collaborations are composed, their virtual classes with the same names are again composed by mixin composition. For example,
Expressions.Expr is evaluated as
ExprEval.Expr & ExprFormat.Expr, therefore it has method
eval() as well as method
format().
Mixin composition propagates infinitely deep and merges all virtual classes at all levels of nesting.
Merging Inheritance Hierarchies
An interesting point about mixin composition is the way how it manages inheritance links between virtual classes. A collaboration can refine its supercollaboration with new virtual classes and new inheritance links. Then the inheritance hierarchies of such refinements can be merged using mixin composition.
We will illustrate hierarchy refinement and merging with collaborations implementing various graphical shapes. At first we define
ShapeCore collaboration, which contains classes
Shape,
Point and
MultiPointShape:
public cclass ShapeCore {
abstract public cclass Shape {
abstract public Point getPos();
abstract public void moveTo(Point newPos);
/* ... */
}
public cclass Point {
protected int _x, _y;
public int getX() { return _x; }
public int getY() { return _y; }
/* ... */
}
abstract public cclass MultiPointShape extends Shape {
protected List _points = null;
public Point getPos() { return _points.getAt(0); };
public void moveTo(Point newPos) {
/* shift all points so that _points[0] is at newPos */;
}
/* ... */
}
}
Shape is the base class for all possible kinds of shapes.
MultiPointShape is an abstract class, which manages a list of points, and can be used to implement shapes, defined by multiple points.
Now let's define sample refinements of
ShapeCore, which implement concrete shapes.
PolygonCore implements various kinds of polygon shapes (free shaped polygons, rectangles, regular polygons) and provides an abstract
Polygon class to access all polygons uniformly:
public cclass PolygonCore extends ShapeCore {
abstract cclass Polygon extends Shape {
abstract public int getVerticeCount();
/* ... */
}
cclass FreePoly extends Polygon & MultiPointShape {
public int getVerticeCount() { return _points.getSize(); }
/* ... */
}
cclass Rectangle extends Polygon {
protected int _height, _width;
public int getVerticeCount() { return 4; }
/* ... */
}
cclass RegularPoly extends Polygon {
protected int _n, _radius;
public int getVerticeCount() { return _n; }
/* ... */
}
}
The diagram bellow display inheritance links between virtual classes of
PolygonCore collaboration. Implicit classes and implicit inheritance links are displayed by with lines:
Let's say
LineCore is analogous exension of
ShapeCore to implement various kinds of lines. It can have following inheritance hierarchy:
Now we can apply mixin composition to generate a collaboration, which contains all kinds of concrete shapes. In our case we define
AllShapes as mixin composition of
PolygonCore and
LineCore:
public cclass AllShapes extends PolygonCore & LineCore {
}
Mixin composition joins virtual classes with the same name and builds a common inheritance hierarchy, which has the inheritance links of all supercollaborations. So
AllShapes will have a superset of
PolygonCore and
LineCore inheritance links:
The joined inheritance hierarchy can be further refined. Let’s consider another example.
ArrowCore is a collaboration, which implements generic functionality of arrow shapes:
public cclass ArrowCore extends ShapeCore {
abstract public cclass ArrowShape extends Shape {
protected ArrowEnd _fromEnd = null;
protected ArrowEnd _toEnd = null;
abstract public Point getFromPt();
abstract public Point getToPt();
/* ... */
}
abstract public cclass ArrowEnd extends Shape {
ArrowShape _arrow;
abstract public Point adjustTo(Point endPt, Point dirPt);
/* ... */
}
}
Now we can merge
ArrowCore with our collaborations for concrete shapes. In the new subcollaboration we can define additional inheritance links to add arrow fuctionality to
Line and
PolyLine class. We implement one concrete arrow end class by reusing functionality of
FreePoly:
public cclass SimpleArrows extends PolygonCore & LineCore & ArrowCore {
cclass Line extends ArrowShape {
public Point getFromPt() { return _startPt; }
/* ... */
}
cclass PolyLine extends ArrowShape {
public Point getFromPt() { return _points.getAt(0); }
/* ... */
}
cclass TriangleEnd extends ArrowEnd & FreePoly {
public Point adjustTo(Point endPt, Point dirPt) {
/* set the polygon points so that they represent triangle
with symmetry axis (endPt, dirPt) and one point at endPt */
}
/* ... */
}
}
The inheritance hierarchy of
SimpleArrows contains the inheritance links of all three supercollaborations as well the newly introduced links:
<< Dependent Types | Contents | Using Caesar >>