Refactoring techniqueMoving Features between Objects
Remove Middle Man
Let clients call the delegate directly when a class does little more than forward calls to it.
Problem
A class has grown a forwarding method for every method of its delegate. Each new feature on the delegate requires a matching pass-through, and the class adds nothing but a hop.
Solution
Expose the delegate and have clients call it directly. Delete the forwarding methods.
Before
class Warehouse {
constructor(private stock: StockLedger) {}
quantityOf(sku: string) { return this.stock.quantityOf(sku); }
reserve(sku: string, qty: number) { return this.stock.reserve(sku, qty); }
release(sku: string, qty: number) { return this.stock.release(sku, qty); }
receive(sku: string, qty: number) { return this.stock.receive(sku, qty); }
adjust(sku: string, delta: number, reason: string) {
return this.stock.adjust(sku, delta, reason);
}
}
// client
warehouse.reserve("SKU-1182", 3);After
class Warehouse {
constructor(readonly stock: StockLedger) {}
}
// client
warehouse.stock.reserve("SKU-1182", 3);Why
- Adding a ledger operation no longer needs a twin method on
Warehouse. - Readers see which object actually does the work.
- Less code to test and keep in sync.
Steps
- Add an accessor (or a
readonlyfield) that exposes the delegate. - For one forwarding method, replace each client call with a direct call on the delegate. Run the tests.
- Delete that forwarding method. Run the tests.
- Repeat for the remaining forwarders.
- If the server is now empty of behavior, consider Inline Class.
Watch out
- Keep forwarders that add something: access control, logging, validation, transactions, or translation between models. Those are a Proxy or Facade, not a middle man.
- Keep the hop if the delegate is an implementation detail you may swap, or if exposing it would let clients bypass invariants the server enforces.
- Removing forwarders from a published API is a breaking change. Deprecate first.
- The inverse is Hide Delegate. Balance the two: hide what is likely to change, expose what is stable.
Related
- Fixes Middle Man.
- Inverse: Hide Delegate.
- Inline Method removes single forwarders; Inline Class removes an empty server.
Agent checklist
- The forwarders add no logic, checks or translation.
- Exposing the delegate does not let clients break the server's invariants.
- All clients of each forwarder were redirected before deleting it.
- Public API removals were deprecated or versioned.
- Tests pass.
Treats these smells
See also
- Hide DelegateAdd a method on the server object that forwards to its delegate, so clients stop navigating through it to reach the delegate.
- Inline MethodReplace calls to a function whose body is as clear as its name with the body itself, then delete the function.
- Inline ClassFold a class that does too little into the class that uses it, moving its fields and methods across and deleting it.
- ProxyPut a stand-in with the same interface in front of an object to control access to it: create it lazily, check permissions, or reach it remotely.
- FacadeGive a complex subsystem one small entry point for a common task, so callers stop orchestrating its classes and calling them in the right order.
Original writing and examples. Topic coverage follows Refactoring.Guru, which explains this topic with its own illustrations.