# Hide Delegate

> Add a method on the server object that forwards to its delegate, so clients stop navigating through it to reach the delegate.

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

## Problem

Clients call `a.getB().getC().doThing()`. They depend on the structure between `a`, `b` and `c`, so restructuring any link breaks every chain in the codebase.

## Solution

**Give the first object a method that does the navigation internally, and have clients call that method instead.** The client then knows one object, not the path behind it.

## Before

```ts
// In several controllers and jobs:
const approverEmail = order.getAccount().getBillingContact().email;
const region = order.getAccount().getBillingContact().address.region;

class Order {
  constructor(private account: Account) {}
  getAccount(): Account { return this.account; }
}
```

## After

```ts
const approverEmail = order.billingContactEmail();
const region = order.billingRegion();

class Order {
  constructor(private account: Account) {}

  billingContactEmail(): string {
    return this.account.getBillingContact().email;
  }

  billingRegion(): string {
    return this.account.getBillingContact().address.region;
  }
}
```

## Why

- If billing contacts move from `Account` to a separate `BillingProfile`, only `Order` changes.
- Clients express intent (`billingRegion()`) instead of navigation.
- Fewer types appear in client imports, so tests need fewer mocks.

## Steps

1. Find a chain clients repeat. Pick the object they start from as the server.
2. Add a method on the server that performs the navigation and returns the needed value or performs the action.
3. Replace one client chain with the new method. Run the tests.
4. Replace the remaining chains.
5. If no client still needs the delegate itself, remove or narrow the server's accessor (`getAccount()`).

## Watch out

- Don't hide every delegate. Each forwarding method enlarges the server. When most of the server's methods are forwarders, it has become a [Middle Man](https://aipatterns.dev/refactoring/smells/middle-man.md).
- Chains through plain data structures or fluent builders (`query.where(...).orderBy(...)`) are not this smell. Leave them.
- Forwarding methods should stay thin. If logic accumulates in them, it may belong in the delegate.
- The inverse is [Remove Middle Man](https://aipatterns.dev/refactoring/techniques/remove-middle-man.md). Use it when forwarding has taken over.

## Related

- Fixes [Message Chains](https://aipatterns.dev/refactoring/smells/message-chains.md) and [Inappropriate Intimacy](https://aipatterns.dev/refactoring/smells/inappropriate-intimacy.md).
- Inverse: [Remove Middle Man](https://aipatterns.dev/refactoring/techniques/remove-middle-man.md).
- [Facade](https://aipatterns.dev/design-patterns/facade.md) applies the same idea to a whole subsystem.

## Agent checklist

- [ ] The chain appears in more than one client, or its structure is likely to change.
- [ ] The new method is named for what the client wants, not the path it takes.
- [ ] All occurrences of the chain were replaced.
- [ ] The server did not turn into a class of mostly forwarders.
- [ ] Tests pass.
