Что такое абстрактные классы и когда они применяются?

В TypeScript абстрактные классы представляют собой инструмент для проектирования архитектуры приложения. Они позволяют задавать общий каркас для будущих классов-наследников, определяя базовое поведение и контракты, которые должны быть реализованы в конкретных реализациях. Такой подход особенно полезен, когда нужно объединить схожую функциональность и одновременно оставить пространство для специализации.

Определение абстрактных классов

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

abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log("Moving...");
}
}

В этом примере makeSound — абстрактный метод, который должен быть реализован в каждом классе-наследнике. А метод move содержит реализацию и будет доступен для всех наследников без необходимости его переопределять.

Особенности абстрактных классов

  1. Абстрактный класс может содержать как обычные свойства и методы, так и абстрактные.

  2. Если в классе есть хотя бы один абстрактный метод, весь класс должен быть объявлен как abstract.

  3. Наследники абстрактного класса обязаны реализовать все абстрактные методы, иначе они сами станут абстрактными.

  4. Абстрактные классы могут использовать модификаторы доступа (public, protected, private), чтобы управлять доступностью свойств и методов.

Пример применения

Допустим, мы проектируем систему для разных видов животных. У всех есть общие характеристики (например, движение), но звуки у каждого вида свои.

abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log("Animal is moving");
}
}
class Dog extends Animal {
makeSound(): void {
console.log("Woof!");
}
}
class Cat extends Animal {
makeSound(): void {
console.log("Meow!");
}
}
const dog = new Dog();
dog.makeSound(); // "Woof!"
dog.move(); // "Animal is moving"

В этом примере класс Animal задает общий контракт, а конкретные классы Dog и Cat реализуют специфическое поведение.

Когда применять абстрактные классы

  • Когда нужно определить общий интерфейс и частично реализовать его.

  • Когда в проекте есть иерархия объектов с одинаковыми базовыми свойствами и методами, но разным специализированным поведением.

  • Когда требуется избежать дублирования кода в разных классах.

  • Когда логично предоставить общий набор базовых методов (например, логирование, валидация), но детали реализации должны зависеть от конкретного класса-наследника.

Таким образом, абстрактные классы хорошо подходят для проектирования архитектуры, где есть чёткая иерархия и необходимость в повторном использовании базовой логики при одновременной гибкости для реализации конкретного поведения.