Геттеры и сеттеры

Геттеры и сеттеры

Технический фундамент контролируемого доступа

Геттеры и сеттеры решают задачу инкапсуляции на уровне свойств:

  • чтение и запись выглядят как доступ к полю;
  • под капотом можно валидировать, нормализовать и вычислять значения;
  • интерфейс объекта остается стабильным даже при смене внутренней структуры;
  • важно избегать тяжелых side effects внутри getter.

Это делает модель устойчивой к ошибкам ввода и упрощает эволюцию API класса.

Зачем геттеры и сеттеры нужны на практике

Иногда прямой доступ к свойствам класса слишком опасен: можно записать невалидное значение, сломать бизнес-логику или нарушить формат данных. Геттеры и сеттеры позволяют контролировать чтение и запись, не ломая удобный синтаксис доступа как к обычному полю.

Ключевой момент: get и set выглядят как свойства, но под капотом это методы с дополнительной логикой.

Проверь себя: почему полезно иметь проверку при записи email, даже если поле публично?

Базовый синтаксис

class User {
  constructor(name) {
    this._name = name;
  }

  get name() {
    return this._name;
  }

  set name(value) {
    if (typeof value !== 'string' || value.trim() === '') {
      throw new Error('Имя должно быть непустой строкой');
    }
    this._name = value;
  }
}

const user = new User('Анна');
console.log(user.name); // вызов getter
user.name = 'Ира'; // вызов setter

Смотри, что важно: снаружи это выглядит как обычное свойство, но ты получаешь контроль над логикой.

Смотри, что важно: setter не обязателен. Если у свойства есть только get, то оно становится read-only (в классах включен strict mode, поэтому присваивание без setter обычно приводит к ошибке).

class UserId {
  constructor(id) {
    this._id = id;
  }
  get id() {
    return this._id;
  }
}

const u = new UserId(1);
// u.id = 2; // TypeError: property has only a getter

Почему часто используют _ в именах

_name это соглашение, что поле внутреннее и напрямую трогать его не стоит. Это не строгая приватность, но хороший сигнал для команды.

Здесь часто путаются: если в setter написать this.name = value, получится бесконечная рекурсия. Нужно писать во внутреннее поле (this._name).

set name(value) {
  this.name = value; // Ошибка: setter вызывает сам себя
}

Геттеры для вычисляемых значений

Getter можно использовать не только для возврата поля, но и для расчетов.

class Product {
  constructor(price, discountPercent) {
    this.price = price;
    this.discountPercent = discountPercent;
  }

  get finalPrice() {
    return this.price - (this.price * this.discountPercent) / 100;
  }
}

const product = new Product(1000, 15);
console.log(product.finalPrice); // 850

Дополнительный пример: нормализация email через setter.

class Account {
  constructor(email) {
    this._email = '';
    this.email = email;
  }

  get email() {
    return this._email;
  }

  set email(value) {
    if (typeof value !== 'string' || !value.includes('@')) {
      throw new Error('Некорректный email');
    }
    this._email = value.trim().toLowerCase();
  }
}

Новый термин: вычисляемое свойство - значение, которое вычисляется динамически при чтении.

Проверь себя: почему finalPrice удобнее как getter, чем как обычный метод getFinalPrice() в некоторых командах?

Микро-сценарии из продукта

  1. Профиль пользователя.
  • setter email нормализует регистр и проверяет формат;
  • getter displayName возвращает подготовленное имя для UI.
  1. Модель заказа.
  • setter status разрешает только допустимые статусы;
  • getter isCompleted показывает готовность заказа.

Такой подход сохраняет данные в консистентном состоянии.

Частые ошибки новичков

  • Делать в getter тяжелые операции или сетевые вызовы.
  • Писать setter без валидации там, где данные критичны.
  • Использовать геттеры/сеттеры там, где обычного свойства достаточно.
  • Нарушать контракт и менять типы возвращаемых значений.

Анти-провал: используй get/set там, где действительно нужна логика контроля или вычисления, а не "для красоты".

Что будет, если изменить входные данные

Если записать user.name = '', setter выбросит ошибку и защитит объект от невалидного состояния. Если записать user.name = 'Марк', значение обновится корректно. Это предсказуемая защита инвариантов модели.

Проверь себя: зачем валидировать значение в setter, если фронтенд уже проверяет форму?

Краткий итог

  • Геттеры и сеттеры дают контроль над чтением и записью свойств.
  • Внешне доступ остается удобным как к обычным полям.
  • Setter подходит для валидации и нормализации входа.
  • Getter удобен для вычисляемых свойств без лишних вызовов.
  • Используй get/set осознанно, когда это реально повышает надежность модели.