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
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
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
- Confirm the class has one user, or that every user can be redirected. Check for uses in DI configuration, serialization and tests.
- In the absorbing class, create public members for each public member of the small class. Make them delegate. Run the tests.
- Redirect external callers of the small class to the absorbing class. Run the tests.
- Move fields and methods across with Move Field and Move Method, one at a time.
- 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, anEmailAddress). 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.
Related
- Fixes Lazy Class and Speculative Generality.
- Inverse: Extract Class.
- Inline Method is the method-level counterpart; Remove Middle Man handles classes that only delegate.
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
- Lazy ClassA class does too little to justify the file, name and indirection it costs; fold it into its caller or delete it.
- Speculative GeneralityAbstractions, hooks and parameters built for future needs that never arrived make code harder to read without serving any current use.
- Shotgun SurgeryOne logical change forces small edits in many classes, so one missed edit ships an inconsistent system.
See also
- Extract ClassSplit a class that does two jobs by moving one job's fields and methods into a new class it holds a reference to.
- Inline MethodReplace calls to a function whose body is as clear as its name with the body itself, then delete the function.
- Move MethodMove a method to the class whose data it uses most, and leave a delegating call or update the callers.
- Move FieldMove a field to the class that uses it most, updating every reader and writer to reach it there.
- Remove Middle ManLet clients call the delegate directly when a class does little more than forward calls to it.
Original writing and examples. Topic coverage follows Refactoring.Guru, which explains this topic with its own illustrations.