Consider the simple class hierarchy of the design model in Figure 16.1 above, showing
a disjoint segmentation of the class Book. In such a case,
whenever there is only one level (or there are only a few levels) of
subtyping and each subtype has only one (or a few) additional properties,
it's an option to re-factor the class hierarchy by merging all the
additional properties of all subclasses into an expanded version of the
root class such that these subclasses can be dropped from the model,
leading to a simplified model.
This Class Hierarchy
Merge design pattern comes in two forms. In its simplest form,
the segmentations of the original class hierarchy are disjoint, which
allows to use a single-valued category
attribute for representing the specific category of each instance of the
root class corresponding to the unique subclass instantiated by it. When
the segmentations of the original class hierarchy are not disjoint, that
is, when at least one of them is overlapping, we need to use a
multi-valued category
attribute for representing the set of types instantiated by an object. We
only discuss the simpler case of Class Hierarchy
Merge re-factoring for disjoint segmentations, where we take
the following re-factoring steps:
Add an enumeration datatype that
contains a corresponding enumeration literal for each segment
subclass. In our example, we add the enumeration datatype
BookCategoryEL.
Add a category attribute to the root class with
this enumeration as its range. The category attribute
is mandatory [1], if the segmentation is complete, and optional
[0..1], otherwise. In our example, we add a category
attribute with range BookCategoryEL to the class
Book. The category attribute is optional
because the segmentation of Book into
TextBook and Biography is
incomplete.
Whenever the segmentation is rigid, we designate the
category attribute as frozen, which means that it can
only be assigned once by setting its value when creating a new
object, but it cannot be changed later.
Move the properties of the segment subclasses to the root
class, and make them optional. We call these
properties, which are typically listed below the
category attribute, segment properties. In our
example, we move the attributes subjectArea from
TextBook and about from
Biography to Book, making them optional, that is [0..1].
Add a constraint (in an invariant box attached to the expanded
root class rectangle) enforcing that the optional subclass
properties have a value if and only if the instance of the root
class instantiates the corresponding category. In our example, this
means that an instance of Book is of category
"TextBook" if and only if its attribute subjectArea has
a value, and it is of category "Biography" if and only if its
attribute about has a value.
Drop the segment subclasses from the model.
In the case of our example, the result of this design re-factoring
is shown in Figure 16.7 below. Notice that the constraint (or
"invariant") represents a logical sentence where the logical operator
keyword "IFF" stands for the logical equivalence operator "if and only if"
and the property condition prop=undefined tests if the
property prop does not have a value.