|
<< Dependent Types | Contents | Using Caesar >>
Composing CollaborationsPropagating Mixin CompositionIn the previous chapter we showed how virtual classes can be used to introduce new functionality to class collaborations. We definedExprFormat 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 HierarchiesAn 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 defineShapeCore 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 >> |