Абстрактные классы, геттеры и сеттеры

Шаблон-заготовка, который нельзя создать напрямую, и умные свойства с логикой доступа.

Абстрактный класс — заготовка, которую нельзя инстанцировать; она задаёт общую структуру для наследников.

Абстрактные классы

Иногда базовый класс не должен существовать сам по себе — он лишь общий каркас. Например, «фигура вообще» — это абстракция; реальны только конкретные фигуры. Ключевое слово abstract запрещает создавать объект такого класса:

abstract class Shape {
  abstract area(): number;       // метод без тела — наследник обязан реализовать

  describe(): string {            // обычный метод — общий для всех
    return "Площадь: " + this.area();
  }
}

const s = new Shape();
// Ошибка: Cannot create an instance of an abstract class.

Абстрактный метод area() объявлен без тела. Это контракт: каждый наследник обязан его реализовать, иначе ошибка компиляции.

Наследник реализует абстрактное

class Square extends Shape {
  constructor(private side: number) { super(); }
  area(): number { return this.side ** 2; }
}

const sq = new Square(5);
console.log(sq.describe()); // "Площадь: 25"

Square реализовал area() и сразу получил готовый describe() от родителя. Это удобно: общую логику пишут один раз в абстрактном классе, а различия — в наследниках.

Абстрактный класс или интерфейс?

ИнтерфейсАбстрактный класс
только описание, без кодаможет содержать готовые методы и поля
класс реализует нескольконаследуется только один

Берут абстрактный класс, когда у наследников есть общий код; интерфейс — когда нужен только контракт без реализации.

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

Геттер и сеттер выглядят как обычное свойство, но за обращением скрывается метод. Это позволяет добавить логику при чтении и записи, не меняя синтаксис вызова:

class Temperature {
  private _celsius: number = 0;

  get celsius(): number {
    return this._celsius;
  }

  set celsius(value: number) {
    if (value < -273.15) {
      throw new Error("Ниже абсолютного нуля нельзя");
    }
    this._celsius = value;
  }

  get fahrenheit(): number {
    return this._celsius * 9 / 5 + 32; // вычисляется на лету
  }
}

const t = new Temperature();
t.celsius = 25;            // вызовется сеттер с проверкой
console.log(t.celsius);    // 25 — вызовется геттер
console.log(t.fahrenheit); // 77 — вычисляемое свойство

Снаружи это выглядит как простое присваивание t.celsius = 25, но внутри срабатывает проверка. А fahrenheit — вычисляемое свойство только для чтения: сеттера у него нет, поэтому записать в него нельзя.

Зачем это: валидация и инкапсуляция

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

Итог

  • abstract-класс нельзя создать напрямую; его абстрактные методы обязаны реализовать наследники.
  • Абстрактный класс хранит общий код для наследников; интерфейс — только контракт.
  • Геттеры/сеттеры добавляют логику (валидацию, вычисление) при чтении и записи, сохраняя синтаксис обычного свойства.
Проверьте себя
1. Что нельзя сделать с абстрактным классом?
AУнаследоваться от него
BСоздать его экземпляр через new напрямую
CОбъявить в нём обычные методы
DОбъявить в нём абстрактные методы
2. Что обязан сделать наследник абстрактного класса с абстрактным методом area()?
AМожет его игнорировать
BОбязан реализовать его, иначе ошибка компиляции
CДолжен пометить его private
DДолжен вызвать super.area()
3. Зачем использовать сеттер вместо публичного поля?
AСеттер работает быстрее присваивания
BЧтобы добавить логику (например, валидацию) при записи, сохранив синтаксис обычного свойства
CЧтобы поле стало статическим
DЧтобы запретить чтение значения
Поддержать проект