Replace Subclass with Fields
Before you begin: Inheritance, overriding, constructors, and fields.
Replace Subclass with Fields replaces subclasses that differ only in constant data with one class storing that data. Instead of creating a class for every capacity, color, or label, pass the value to an instance. Fowler’s newer catalog calls this Remove Subclass. 1
Original version: a class for every chest size
public abstract class Chest
{
public abstract int Capacity();
}
public sealed class SmallChest : Chest
{
public override int Capacity() { return 10; }
}
public sealed class LargeChest : Chest
{
public override int Capacity() { return 30; }
}class Chest:
def capacity(self):
raise NotImplementedError
class SmallChest(Chest):
def capacity(self):
return 10
class LargeChest(Chest):
def capacity(self):
return 30The subclasses do not implement different storage algorithms. Each supplies one fixed number. Adding a twenty-slot chest would require another class for another number.
Refactored version: capacity is data
public sealed class Chest
{
private readonly int capacity;
public Chest(int capacity) { this.capacity = capacity; }
public int Capacity() { return capacity; }
}Chest small = new Chest(10);
Chest large = new Chest(30);
Console.WriteLine(small.Capacity()); // 10
Console.WriteLine(large.Capacity()); // 30The behavior is one getter; only its stored value differs. An additional size no longer requires a subclass declaration.
class Chest:
def __init__(self, capacity):
self._capacity = capacity
def capacity(self):
return self._capacitysmall = Chest(10)
large = Chest(30)
print(small.capacity()) # 10
print(large.capacity()) # 30Each instance stores its own capacity. The object type no longer encodes the size.
Preserve distinctions that matter
Before deleting subclasses, inspect type checks, serialization tags, public constructors, and overrides. A class name can be part of a contract even if its methods look trivial. Callers using SmallChest as an identity or dispatch signal need migration.
The new constructor also permits capacities beyond the two original choices. A factory or validation can restrict that when necessary. This example deliberately demonstrates a more general data representation; it is not an assertion that every integer is a valid production chest size.
Keep subclasses when their distinct behavior or substitutable domain roles are meaningful. Remove them when they merely turn a table of data into a collection of files.