Extract Subclass
Before you begin: Inheritance, overriding, and constructors.
Extract Subclass creates a specialized subtype for a meaningful subset of instances. Behavior specific to that subset moves out of the general class. The new subtype must still make sense wherever the original contract is expected. 1
Original version: a flag selects special notice behavior
public sealed class Notice
{
private readonly string message;
private readonly bool urgent;
public Notice(string message, bool urgent)
{
this.message = message;
this.urgent = urgent;
}
public string Render() { return urgent ? "URGENT: " + message : message; }
}class Notice:
def __init__(self, message, urgent):
self._message = message
self._urgent = urgent
def render(self):
return "URGENT: " + self._message if self._urgent else self._messageThe distinction is stored inside every notice. A richer urgent notice might also require different delivery or acknowledgement behavior.
Refactored version: ordinary and urgent notices share a contract
public class Notice
{
protected string Message { get; }
public Notice(string message) { Message = message; }
public virtual string Render() { return Message; }
}
public sealed class UrgentNotice : Notice
{
public UrgentNotice(string message) : base(message) { }
public override string Render() { return "URGENT: " + Message; }
}class Notice:
def __init__(self, message):
self._message = message
def render(self):
return self._message
class UrgentNotice(Notice):
def render(self):
return "URGENT: " + self._messageThe caller chooses the specialized class at construction. Calling the same rendering operation still returns the appropriate text, without a flag check in the general implementation.
Do not extract a subtype for every data variation
For this one prefix, a field or formatting function may be simpler. A subclass earns its place when the specialization has coherent behavior and a stable “is a” relationship—not merely because two instances contain different data.
If urgency can change on the same notice, a replaceable behavior object may be more suitable than a fixed subclass. If urgency and delivery channel vary independently, multiplying subclasses for every combination can become unwieldy.
Migrate constructors and verify old outcomes before adding new specialization behavior. The subtype should not unexpectedly reject valid inputs, change required side effects, or otherwise break the caller’s original expectations.
Sources
- Martin Fowler — Replace Type Code with Subclasses, which includes Extract Subclass in the catalog’s earlier terminology.