Class Hierarchy Merge
Let's learn about the class hierarchy merge design pattern.
We'll cover the following
The Class Hierarchy Merge design pattern
Consider the simple class hierarchy of the design model in the
Book
. Whenever there’s only one level (or a few levels) of subtyping, and each subtype has only one (or a few) additional properties, we can refactor the class hierarchy. This can be done if we merge all the additional properties of all subclasses into an expanded version of the root class so that these subclasses can be dropped from the model. This will lead 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 us to use a single-valued category
attribute to represent the specific category of each instance of the root class that corresponds 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, then we need to use a multivalued category
attribute to represent the set of types instantiated by an object. We only discuss the simpler case of Class Hierarchy Merge refactoring for disjoint segmentations. For this case, we take the following refactoring steps:
- Add an enumeration data type 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. If the segmentation is complete, thecategory
attribute is mandatory [1] and optional [0…1]. In our example, we add acategory
attribute with the rangeBookCategoryEL
to the classBook
. Thecategory
attribute is optional because the segmentation of Book intoTextBook
andBiography
is incomplete. - Whenever the segmentation is rigid, we designate the
category
attribute as frozen. This means that it can only be assigned once. This is when we set its value after we’ve created a new object. It can’t be changed later. - Move the properties of the segment subclasses to the root class, and make them optional. We call these properties segment properties. They’re typically listed below the
category
attribute. In our example, we move the attributessubjectArea
fromTextBook
andabout
fromBiography
toBook
. This makes them optional, or [0…1]. - Add a constraint in the “invariant” box attached to the expanded root class rectangle. This makes sure 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 the categoryTextBook
if and only if its attributesubjectArea
has a value. It’s of the categoryBiography
if and only if it’s about` attribute has a value. - Drop the segment subclasses from the model.
The result of this design refactoring is shown in the figure below. 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
doesn’t have a value.
Get hands-on with 1400+ tech skills courses.