Гид по технологиям

Инкапсуляция в TypeScript: классы, геттеры и сеттеры

5 min read Программирование Обновлено 12 Apr 2026
Инкапсуляция в TypeScript: геттеры и сеттеры
Инкапсуляция в TypeScript: геттеры и сеттеры

красная и синяя таблетки на синем фоне

Инкапсуляция означает удержание чего-то в изоляции — если поместить что-то в капсулу, внешнему миру недоступно его внутреннее состояние. В объектно-ориентированном программировании инкапсуляция помогает держать сложный код управляемым и предсказуемым.

Зачем нужны классы

Представьте приложение для контактного зоопарка с огромной кодовой базой. В нём есть важный объект, центральный для логики, например animal. Что если каждый компонент программы мог бы напрямую читать и изменять этот объект?

Свободный доступ приведёт к путанице. Если поросёнок использует animal для определения своих свойств, тогда animal приобретёт атрибуты поросёнка. Потом коза использует тот же объект — и вы получите неожиданные мутации.

Пример на JavaScript/TypeScript, иллюстрирующий проблему:

    var animal = {name: "piglet", legs: 4, color: "pink", decoration: "snout"}
animal.name = "goat"
animal.decoration = "horns"

В результате у вас окажутся розовые козы и поросята с рогами. В больших проектах отследить, какой именно участок кода изменил объект, может занять сотни часов. После этого придётся писать дополнительные заглушки и проверки, чтобы ограничить побочные эффекты.

Решение — определять объекты через классы. Любая часть кода может создать экземпляр класса. Инстанцирование гарантирует, что у каждого объекта свои собственные свойства и они не будут вмешиваться друг в друга.

Классов недостаточно — переменные объекта тоже должны быть инкапсулированы

Создадим класс, описывающий наших животных:

    class Animal {
  name: string;
  legs: number;
  color: string;
  decoration: string;

  constructor(name: string, legs: number, color: string, decoration: string) {
    this.name = name;
    this.legs = legs;
    this.color = color;
    this.decoration = decoration;
  }
}

Создаём пару объектов:

    let babyDuck = new Animal("baby duck", 2, "yellow", "beak");
let bunny = new Animal("bunny", 4, "gray", "floppy ears");

Так можно добавлять животных сколько угодно — до тех пор, пока кто-то случайно не изменит их свойства:

серо-белый кролик на траве

Например:

    bunny.color = "black";
bunny.legs = 8;

Паучьи кролики — не лучшая идея. Это всё та же проблема: объекты можно изменить извне. Решение — сделать поля приватными и предоставить контролируемый доступ через геттеры и сеттеры.

Вот как выглядит класс с приватными полями и контролем для количества ног:

    class Animal {
  private _name: string;
  private _legs: number;
  private _color: string;
  private _decoration: string;

  constructor(name: string, legs: number, color: string, decoration: string) {
    this._name = name;
    this._legs = legs;
    this._color = color;
    this._decoration = decoration;
  }

  get legs() {
    return this._legs;
  }

  set legs(legCount: number) {
    if(legCount > 1 && legCount < 5) {
      this._legs = legCount;
    }
  }
}

Геттеры и сеттеры — это функции, которые читают и изменяют внутренние переменные в контролируемой форме. Сеттеры позволяют наложить ограничения на значения, а геттеры — модифицировать или форматировать данные при выдаче.

Практическое резюме и задания для закрепления

Вот финальный список задач и резюме того, что вы должны сделать:

  • Добавьте геттеры и сеттеры для остальных переменных (_name, _color, _decoration).
  • Верните имя животного как span-тег: llama. Это упражнение показывает, что геттер может форматировать вывод.
  • Измените decoration так, чтобы он поддерживал несколько украшений. Создайте соответствующие геттер и сеттер для массива украшений.

Важно: избегайте глобальных переменных. Если необходимо делиться состоянием между объектами, используйте статические поля класса или отдельные сервисы с чётким API.

Полезные шаблоны и приёмы

Приведу дополнительные шаблоны и практические советы, которые пригодятся при работе с инкапсуляцией в TypeScript.

Приватные поля и readonly

  • Используйте private или private readonly, если поле не должно меняться после создания.
  • В новых версиях TypeScript можно использовать синтаксис приватных полей с #, он обеспечивает приватность на уровне рантайма (ESNext).

Пример с readonly и массивом украшений:

class AnimalV2 {
  private _name: string;
  private _legs: number;
  private _color: string;
  private _decorations: string[];

  constructor(name: string, legs: number, color: string, decorations: string[]) {
    this._name = name;
    this._legs = legs;
    this._color = color;
    this._decorations = decorations;
  }

  get name(): string {
    return `${this._name}`;
  }

  get decorations(): string[] {
    return [...this._decorations]; // возвращаем копию
  }

  set decorations(items: string[]) {
    // валидация: максимум 5 украшений
    if (items.length <= 5) {
      this._decorations = [...items];
    }
  }
}

Обратите внимание: геттер decorations возвращает копию массива, чтобы внешний код не мог напрямую мутировать внутренний массив.

Модель мышления и эвристики

  • Правило одного владельца состояния: каждая сущность должна владеть своим состоянием; изменение состояния другой сущности должно идти через явные методы или события.
  • Минимизация привилегий: давайте доступ только тем частям кода, которым он действительно нужен.
  • Явная валидация: ставьте проверки в сеттерах и в конструкторе.

Когда инкапсуляция не решит проблему

  • Если у вас глобальные однострочные скрипты или утилиты, классы могут усложнить простую логику.
  • Для некоторых архитектур (функциональный стиль, data-oriented) композиция и чистые функции предпочтительнее классов.
  • В системах с высокой частотой обновлений состояние можно держать в отдельных стореджах/редьюсерах, а не в экземплярах классов.

Контрпример: если вам нужно очень часто копировать данные между миллионами простых структур, накладные расходы методов доступа могут быть заметны; в таких случаях лучше использовать простые структуры данных и чистые функции.

Альтернативы классам

  • Композиция + фабрики: функции, которые создают объекты с замыканиями (closures) для приватности.
  • Модули (namespaces) и замыкания: переменные модуля недоступны извне, пока вы не экспортируете API.
  • Immutable-объекты: вместо изменения состояния создавайте новые объекты.

Простой пример замыкания как альтернативы:

function createAnimal(name, legs) {
  let _name = name;
  let _legs = legs;
  return {
    getName() { return _name; },
    setLegs(n) { if (n > 1 && n < 5) _legs = n; },
    getLegs() { return _legs; }
  };
}

Миграция старой кодовой базы — мини-методология

  1. Найдите самые изменяемые объекты (частые глобальные мутации).
  2. Вокруг них создайте классы/обёртки с приватными полями и контролируемым API.
  3. Пошагово заменяйте прямые обращения на вызовы методов класса.
  4. Покройте изменения тестами: юнит-тесты для геттеров/сеттеров и интеграционные тесты на сценарии.
  5. Рефакторьте и удаляйте глобальные состояния по мере уверенности в тестах.

Критерии приёмки

  • Все прямые обращения к ранее глобальным полям заменены методами/геттерами.
  • Сценарии, которые ранее ломали объекты (например, некорректное число ног), теперь предотвращаются сеттерами.
  • Появились юнит-тесты, подтверждающие валидацию в сеттерах.

Роль-based checklists

Developer:

  • Внедрять приватные поля и сеттеры для критичных состояний.
  • Не возвращать внутренние ссылки на изменяемые объекты (возвращайте копии).

Code reviewer:

  • Проверять, что геттеры/сеттеры валидируют данные.
  • Убедиться, что нет прямых мутаций приватных полей извне.

Architect:

  • Решать, где использовать классы, а где — функциональный стиль.
  • Определять правила владения состоянием и границы модулей.

Небольшой глоссарий в одну строку

Инкапсуляция — изоляция внутреннего состояния объекта и предоставление контролируемого API для доступа.

Краткое повторение

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

Примечание: переводите существующие глобальные состояния в управляемые сервисы постепенно и с тестами.

Сводка и дальнейшие шаги

  • Начните с ключевых объектов, инкапсулируйте поля, добавьте проверки.
  • Для коллекций возвращайте копии, чтобы не раскрывать внутреннее состояние.
  • Обсуждайте с командой архитектурные решения и документируйте правила владения состоянием.
Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Встроенные команды оболочки в Linux — как проверить
Linux

Встроенные команды оболочки в Linux — как проверить

Как настроить пользовательские профили Fujifilm
Фотография

Как настроить пользовательские профили Fujifilm

Настройка рабочего стола Windows 8
Windows

Настройка рабочего стола Windows 8

Как делать скриншоты в Windows 11 и 10
Windows

Как делать скриншоты в Windows 11 и 10

Сделать iPhone похожим на Android — руководство
Мобильные телефоны

Сделать iPhone похожим на Android — руководство

Заменить HDD на SSD в ноутбуке — руководство
Апгрейд

Заменить HDD на SSD в ноутбуке — руководство