ColdFrame: Generalizations

Motivation

The thinking behind ColdFrame is that instead of making code models you should be making what Martin Fowler, in Appendix A1.1, calls "implicit specification models".

One way of looking at it is that modelling ought to be, as far as reasonable, independent of the software architecture (both the implementation language and the support infrastructure). What we are modelling is generalization. We may be able to implement generalization using inheritance in our implementation language, but there may be practical difficulties; perhaps we have to implement in C; maybe we're using an SQL database to implement persistence; our implementation language may not support multiple inheritance.

Another aspect is dynamic versus static classification.

The major use of generalization (well, specialization) is to classify aspects of the subject matter. Inheritance, as implemented in C++ or other object-oriented programming languages, is fine for static classification (subtyping Animal as Pig, Goat, Cow for example).

However, a very useful analysis technique is dynamic classification (Fowler A1.3). Rent-a-Wreck has a fleet of vans, which are sometimes on hire. We might want to model this as Customer hires Van, which is biconditional (a known Customer may not have any Van on hire at the moment; a Van may not be hired to anyone at the moment) and hence a little vague. The model can be made crisper by subtyping Van into Hired Van and Pool Van; now all Hired Vans have a Customer, so the code doesn't need to check whether there's a Customer or not.

The attribute termination only has meaning if the van is on hire (it's the date when it's due back), so we shouldn't leave it lying around in the parent Van class. Also, the association r3, Hired Van is-on-loan-to Customer is unconditional, which is good.

Note also the way r1, which maintains the service history of the Van, is independent of whether the Van is currently hired out or in the pool.

The problem with implementing this model using programming language inheritance arises when the Van changes from being Pooled to being Hired. There's only one code object, so you have to create a new HiredVan, copy the relevant attributes from the old PoolVan to it, break any associations with other objects (for instance, the Service history), delete the old PoolVan and remake the associations.

ColdFrame adopts a halfway-house approach to this, combining the Executable UML (originally Shlaer-Mellor) view of generalization with dispatching and inherited operations.

Of course, there is a cost to dynamic classification; you have to decide whether increased precision in the model is worth the extra run time costs of maintaining the model.

At present (10.ii.12), ColdFrame expects the set of child instances to be complete, which is why Pool Van appears in the model above. Every Van must be either a Hired Van or a Pool Van. This only actually shows up in implementing dispatching operations; if you try calling a dispatching operation for an instance without a current child you'll get a Constraint_Error. Often you'll find something interesting to say about all the child classes; in the example, it seems likely that only Pool Vans can be sent for servicing.

Modelling

You must not specify identifiers for child classes in inheritance relationships.

ColdFrame supports polymorphism.

ColdFrame supports multiple inheritance, but not partitioned inheritance. In spite of this, you need to name all the generalization/specialization links in your class diagram, and all the links for a particular relationship must have the same name (r2 in the diagram). Note that ArgoUML (at 0.34) implements UML 1.4, in which labels on generalization links represent the discriminator rather than the generalization name. ColdFrame will use either the generalization name or the discriminator if only one is present; if both are present, they must be the same.

Translation

Basic mapping

Single inheritance

In the parent class, an inheritance relationship G is mapped onto

From the diagram, we'd get

type R2_Child_Class is
  (Hired_Van_T,
   Pool_Van_T,
   Null_T);

type R2_Child (Current : R2_Child_Class := Null_T) is record
   case Current is
      when Hired_Van_T =>
         HV : ColdFrame.Instances.Handle;
      when Pool_Van_T =>
         PV : ColdFrame.Instances.Handle;
      when Null_T => null;
   end case;
end record;

procedure Set_R2_Child (This : Handle; To_Be : R2_Child);

function Get_R2_Child (This : Handle) return R2_Child;

In a child class, an inheritance relationship G is mapped onto

The Create operation in a child class fixes up the G_Child record in the parent.

The Delete operation in a parent class deletes the current child, if any, recursively; the Delete operation in a child class nulls out the G_Child record in the parent.

Multiple inheritance

There's no difference between multiple and single inheritance from the point of view of a parent (ColdFrame disallows partitioned inheritance trees, which in any case aren't really related to multiple inheritance).

For children, the implementation for single inheritance is repeated for each parent. Note particularly that this means you get an identifier with one identifying attribute for each parent.

Extended mapping

There's additional support, particularly for dynamic classification (subtype migration).

Operations

Abstract instance operations (isAbstract checked) are dispatched to the current child (skipping down the inheritance tree until a concrete implementation is found), while a child class receives upward-dispatching implementations of all concrete parental instance operations provided that

Attributes

With this translator, the attributes of a parental class aren't visible to children any more than to any other class (in C++ terms, they are private). You can provide «accessor» operations in the parent to give the necessary visibility.

Use

Without the extended mapping, the fact that the relationships are managed using ColdFrame.Instances.Handle means that you need to do view conversions rather more often than is quite friendly.

Child_H := Child.Create
   ((G_Parent => ColdFrame.Instances.Handle (Parent_H)));

If you're doing this a lot you may find a type renaming convenient:

subtype CIH is ColdFrame.Instances.Handle;


Simon Wright