Skip to content

Extract Class

A class has quietly taken on a second responsibility. Pull the fields and methods that serve that second job out into a brand-new class, and give the original class a reference to it. Each class now has one clear reason to change.

This is the cure for the Large Class — and, more precisely, for a class with two responsibilities tangled together. The signs: a subset of fields that always change together, methods that only operate on that subset, or a name that has to use “and” to describe what the class does. When you can draw a clean line through the class’s data, the half on each side wants to be its own type.

A Person that also stores and formats a telephone number. Before, the phone fields and the phone formatting live on Person. After, they form a TelephoneNumber class, and Person simply holds one.

// Before
class Person {
constructor(
public name: string,
public areaCode: string,
public number: string,
) {}
telephoneNumber(): string {
return `(${this.areaCode}) ${this.number}`;
}
}
// After
class TelephoneNumber {
constructor(public areaCode: string, public number: string) {}
toString(): string {
return `(${this.areaCode}) ${this.number}`;
}
}
class Person {
constructor(public name: string, public telephone: TelephoneNumber) {}
telephoneNumber(): string {
return this.telephone.toString();
}
}
classDiagram
  class PersonBefore {
    name
    areaCode
    number
    telephoneNumber()
  }
  class PersonAfter {
    name
    telephone
    telephoneNumber()
  }
  class TelephoneNumber {
    areaCode
    number
    toString()
  }
  PersonAfter --> TelephoneNumber : has a
  PersonBefore ..> PersonAfter : Extract Class
One Person splits into Person plus a TelephoneNumber it owns
  1. Decide how to split the responsibilities, and create a new empty class for the one you are extracting. If the old name no longer fits the remaining class, rename it.
  2. Add a link from the old class to the new one — usually a field holding an instance of the new class.
  3. Move the relevant fields across one at a time, using Move Field, keeping the suite green after each.
  4. Move the methods that belong with those fields across, using Move Function, starting from the lowest-level ones.
  5. Replace the old class’s direct field access with calls through the new instance.
  6. Run your tests after every move. Each step is small enough that a red bar points at one change.
  7. Review the public surface of both classes and narrow it — the extracted class may be able to hide details the original had to expose.

Extract a class when a subset of fields and methods forms an obvious cluster, when a class is hard to summarize without “and”, or when one responsibility changes for entirely different reasons than the rest.

The cost is one more class to name, construct, and navigate, plus a layer of delegation. If a class is not genuinely doing two jobs, extracting only adds ceremony. The inverse is Inline Class: if the new class never grows into its own responsibility, fold it back in.

Which smell does Extract Class primarily address?
What is a strong sign that a class should be split?
Which refactorings do the heavy lifting inside the Extract Class mechanics?
What is the inverse of Extract Class?