# Self Encapsulate Field

> Access a field through its own getter and setter even inside the class, so subclasses or later changes can intercept the access.

- kind: technique
- category: organizing-data
- aliases: Self-Encapsulate Variable
- canonical: https://aipatterns.dev/refactoring/techniques/self-encapsulate-field
- inspired-by: https://refactoring.guru/self-encapsulate-field

## Problem

Methods in a class read and write a field directly. Now you need to add logic whenever that field is accessed: lazy loading, a derived value in a subclass, change tracking, or a move to another object. Every direct access is a place you must edit.

## Solution

**Create a private (or protected) getter and setter for the field and route every access inside the class through them.** Later changes then happen in one place.

## Before

```ts
class Subscription {
  private seats: number;

  constructor(seats: number) {
    this.seats = seats;
  }

  monthlyCost(pricePerSeat: number): number {
    return this.seats * pricePerSeat;
  }

  addSeats(n: number): void {
    this.seats = this.seats + n;
  }

  canInvite(currentMembers: number): boolean {
    return currentMembers < this.seats;
  }
}
```

## After

```ts
class Subscription {
  private _seats!: number; // assigned through the setter below

  constructor(seats: number) {
    this.seats = seats;
  }

  protected get seats(): number {
    return this._seats;
  }

  protected set seats(value: number) {
    if (!Number.isInteger(value) || value < 1) throw new RangeError(`invalid seat count ${value}`);
    this._seats = value;
  }

  monthlyCost(pricePerSeat: number): number {
    return this.seats * pricePerSeat;
  }

  addSeats(n: number): void {
    this.seats = this.seats + n;
  }

  canInvite(currentMembers: number): boolean {
    return currentMembers < this.seats;
  }
}

class TrialSubscription extends Subscription {
  protected override get seats(): number {
    return Math.min(super.seats, 3); // trials cap seats regardless of purchase
  }
}
```

## Why

- Validation now runs on every write, including the constructor.
- A subclass changes how seats are computed by overriding one accessor.
- Moving the field later ([Move Field](https://aipatterns.dev/refactoring/techniques/move-field.md)) means editing two accessors, not every method.

## Steps

1. Rename the field (for example to `_seats`) so direct uses stand out.
2. Add a getter and setter with the original name. Keep their visibility as narrow as the use requires.
3. Replace each direct read and write inside the class with the accessors. Run the tests.
4. Decide whether the constructor should use the setter. Use it if the setter validates; avoid it if a subclass override could run before the subclass is initialized.

## Watch out

- Don't do this for every field by default. Direct access inside a small class is clearer. Apply it when you need the hook now.
- Overridable accessors called from a constructor can run subclass code against a half-built object. That is a real bug source in Java, C# and TypeScript.
- TypeScript accessors that override in a subclass need the base declared as an accessor too; a plain field cannot be overridden by an accessor.
- Inverse: remove the accessors and use the field directly when the hook is no longer needed.

## Related

- Helps with [Data Class](https://aipatterns.dev/refactoring/smells/data-class.md) by giving behavior a place to attach.
- [Encapsulate Field](https://aipatterns.dev/refactoring/techniques/encapsulate-field.md) is the external counterpart for public fields.
- Prepares for [Move Field](https://aipatterns.dev/refactoring/techniques/move-field.md) and [Replace Data Value with Object](https://aipatterns.dev/refactoring/techniques/replace-data-value-with-object.md).

## Agent checklist

- [ ] There is a concrete need for interception (validation, override, lazy load, move).
- [ ] Every in-class access now goes through the accessors.
- [ ] Constructor use of overridable accessors was checked for initialization-order bugs.
- [ ] Accessor visibility is no wider than needed.
- [ ] Tests pass.
