[Latest Guide] Ship faster because you know more, not because you are rushing. Get actionable insights from 7 million commits and 85,000+ software engineers, to increase your team's velocity. Brought to you in partnership with GitPrime.

This article is the third and last part of the refactoring from type codes miniseries.

First, we saw a case where no behavior was modified by the type code values: the type code could be substituted by a single class.

Second, we saw a case where behavior changed: we substituted the different type codes with subclasses of the original class.

In the third case, that we'll see today, some behavior depends on the type code but extending the current class is not possible (because subclassing has been already been used, or as a design choice). This refactoring uses composition instead of inheritance.

What changes from the other two refactorings?

We can make a quick comparison with the other two solutions.

By replacing the type code with a single class, you already use composition and define a type replacing the type code. That type now could be an interface or abstract class, while it was a concrete class in the first refactoring.

By replacing the type code with subclasses, you are using multiple subtypes to model different behavior, eliminating conditional structures like ifs and switch. But in this refactoring they will be subtypes of an external class, not of the original one.

The class you create representing the type code is an implementation of the State or Strategy pattern. In the State case, the object changes often its class, returning a new instance. In the Strategy case, the object changes rarely, and the different implementations contain different algorithms or policies.

Why composing type code objects?

A prerequisite for this refactoring is that different behaviors should be dependent on the type code value.
Moreover, one or more of these conditions should apply:

subclassing is not available due to an already existing hierarchy, or not self-explaining as the class names are unclear and sound forced.

The life cycle of the object is not on your control, so you can't instantiate the subclasses.

The type code changes often, and reinstantiating the right class is a mess for technical reasons (such as your framework or your ORM).

Steps

The steps for applying this refactoring are similar to the
Replace Type Code with Subclasses case, but this time they have to be applied to the State or Strategy object instead that on the main class.

After having moved the variable logic into the Rank hierarchy, we could again remove the getCode() method if not required anymore. We can also turn Rank into an interface, if it is stabilized as a purely abstract class without any code.

[Latest Guide] Ship faster because you know more, not because you are rushing. Get actionable insights from 7 million commits and 85,000+ software engineers, to increase your team's velocity. Brought to you in partnership with GitPrime.