Skip to content
aipatterns.dev

Refactoring techniqueMoving Features between Objects

Inline Class

Fold a class that does too little into the class that uses it, moving its fields and methods across and deleting it.

Problem

A class holds one or two fields and a method or two, and only one other class uses it. It may be a leftover from an earlier split or a placeholder for growth that never came. Every reader has to open a second file to find almost nothing.

Solution

Move all of the small class's features into its single user, then delete it.

Before

TypeScript
class ReportSchedule {
  constructor(private cron: CronSpec, private timezone: string) {}

  nextRunAfter(now: Date): Date {
    return this.cron.next(now, this.timezone);
  }
}

class ScheduledReport {
  constructor(
    public id: string,
    public query: string,
    private schedule: ReportSchedule,
  ) {}

  nextRunAfter(now: Date): Date {
    return this.schedule.nextRunAfter(now);
  }
}

After

TypeScript
class ScheduledReport {
  constructor(
    public id: string,
    public query: string,
    private cron: CronSpec,
    private timezone: string,
  ) {}

  nextRunAfter(now: Date): Date {
    return this.cron.next(now, this.timezone);
  }
}

Why

  • One less type and file to navigate. The schedule logic sits where it is used.
  • Removes a delegating method that added no behavior.
  • Inlining two poorly divided classes can also be the first step toward re-splitting them along a better line.

Steps

  1. Confirm the class has one user, or that every user can be redirected. Check for uses in DI configuration, serialization and tests.
  2. In the absorbing class, create public members for each public member of the small class. Make them delegate. Run the tests.
  3. Redirect external callers of the small class to the absorbing class. Run the tests.
  4. Move fields and methods across with Move Field and Move Method, one at a time.
  5. Delete the small class. Run the tests.

Watch out

  • Don't inline a class that several unrelated classes use, or that models a real domain concept with invariants (a Money, an EmailAddress). Small is not the same as useless.
  • Don't inline when the class sits on a module or package boundary that exists for a reason, such as dependency direction or a published API.
  • Inlining can bloat the receiver. If it is already a Large Class, look for a better home.
  • The inverse is Extract Class.

Agent checklist

  • The class has no independent domain meaning or invariants worth naming.
  • Every user, including configuration, serializers and tests, was found and redirected.
  • Features moved one at a time with tests green after each.
  • The absorbing class did not become a Large Class.
  • The inlined class is deleted.

Treats these smells

See also

Original writing and examples. Topic coverage follows Refactoring.Guru, which explains this topic with its own illustrations.