# Introduce Foreign Method

> Add a helper in your own code that takes an instance of a class you cannot change and provides the method it lacks.

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

## Problem

You need an operation on a library or framework class, such as `Date` or a vendor SDK type, but you cannot add methods to it. The same few lines of workaround are copied into several places.

## Solution

**Write one function in your code that takes the foreign object as its first parameter and does the work.** Name it as if it were a method on that class, and mark it as a stand-in for a missing method.

## Before

```ts
// in the scheduler
const start = new Date(meeting.start);
while (start.getDay() === 0 || start.getDay() === 6) {
  start.setDate(start.getDate() + 1);
}

// in invoicing
const due = new Date(invoice.issuedAt);
due.setDate(due.getDate() + 30);
while (due.getDay() === 0 || due.getDay() === 6) {
  due.setDate(due.getDate() + 1);
}
```

## After

```ts
// dates.ts - foreign method: belongs on Date
export function nextBusinessDay(date: Date): Date {
  const result = new Date(date);
  while (result.getDay() === 0 || result.getDay() === 6) {
    result.setDate(result.getDate() + 1);
  }
  return result;
}

// in the scheduler
const start = nextBusinessDay(new Date(meeting.start));

// in invoicing
const due = new Date(invoice.issuedAt);
due.setDate(due.getDate() + 30);
const dueOnBusinessDay = nextBusinessDay(due);
```

## Why

- The rule exists once. A holiday calendar later changes one function.
- Call sites read as intent.
- Returning a new `Date` removes the mutation hazard of shared instances.

## Steps

1. Identify the repeated workaround and the foreign class it operates on.
2. Create a function in a shared module of your own. Its first parameter is the foreign object; other inputs follow.
3. Move the workaround body into it. Avoid mutating the argument; return a new value where you can.
4. Replace one call site. Run the tests. Replace the rest.
5. Add a short comment that it is a foreign method, so it can move into the class if you ever own it.

## Watch out

- In TypeScript, don't patch `Date.prototype` or other built-ins. It leaks to every module and to third-party code. A plain function is safer.
- If you find yourself writing many foreign methods for the same class, collect them with [Introduce Local Extension](https://aipatterns.dev/refactoring/techniques/introduce-local-extension.md).
- Languages with extension methods (C#, Kotlin, Swift) let you write the same helper with method-call syntax. That is the idiomatic form there, not a different refactoring.
- If you own the class after all, add the method to it with [Move Method](https://aipatterns.dev/refactoring/techniques/move-method.md) instead.
- No inverse; inline the helper back with [Inline Method](https://aipatterns.dev/refactoring/techniques/inline-method.md) if it proves pointless.

## Related

- Fixes [Incomplete Library Class](https://aipatterns.dev/refactoring/smells/incomplete-library-class.md) and [Duplicate Code](https://aipatterns.dev/refactoring/smells/duplicate-code.md).
- Escalate to [Introduce Local Extension](https://aipatterns.dev/refactoring/techniques/introduce-local-extension.md) when helpers multiply.

## Agent checklist

- [ ] The class really cannot be modified (third-party, built-in or generated).
- [ ] The helper takes the foreign object as its first parameter and does not mutate it.
- [ ] All duplicate workarounds now call the helper.
- [ ] No global prototype was patched.
- [ ] Tests pass.
