# Remove Middle Man

> Let clients call the delegate directly when a class does little more than forward calls to it.

- kind: technique
- category: moving-features-between-objects
- canonical: https://aipatterns.dev/refactoring/techniques/remove-middle-man
- inspired-by: https://refactoring.guru/remove-middle-man

## 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

```ts
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

```ts
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

1. Add an accessor (or a `readonly` field) that exposes the delegate.
2. For one forwarding method, replace each client call with a direct call on the delegate. Run the tests.
3. Delete that forwarding method. Run the tests.
4. Repeat for the remaining forwarders.
5. If the server is now empty of behavior, consider [Inline Class](https://aipatterns.dev/refactoring/techniques/inline-class.md).

## Watch out

- Keep forwarders that add something: access control, logging, validation, transactions, or translation between models. Those are a [Proxy](https://aipatterns.dev/design-patterns/proxy.md) or [Facade](https://aipatterns.dev/design-patterns/facade.md), 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](https://aipatterns.dev/refactoring/techniques/hide-delegate.md). Balance the two: hide what is likely to change, expose what is stable.

## Related

- Fixes [Middle Man](https://aipatterns.dev/refactoring/smells/middle-man.md).
- Inverse: [Hide Delegate](https://aipatterns.dev/refactoring/techniques/hide-delegate.md).
- [Inline Method](https://aipatterns.dev/refactoring/techniques/inline-method.md) removes single forwarders; [Inline Class](https://aipatterns.dev/refactoring/techniques/inline-class.md) 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.
