В чем разница между интерфейсом и абстрактным классом?

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

Интерфейс как контракт

Интерфейс в TypeScript используется для описания структуры объекта. Он задает набор свойств и методов, которые должны присутствовать, но не содержит реализации. Интерфейсы применяются исключительно для типизации и проверки структуры во время компиляции.

Пример:

interface Shape {
area(): number;
}
class Circle implements Shape {
constructor(private radius: number) {}
area(): number {
return Math.PI \* this.radius \* this.radius;
}
}

Здесь интерфейс Shape определяет контракт, а класс Circle реализует его. Сам интерфейс не содержит никакой логики.

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

Абстрактный класс, в отличие от интерфейса, может содержать как абстрактные методы (без реализации), так и конкретные методы и свойства с уже написанным кодом. Таким образом, абстрактный класс выступает не только как контракт, но и как основа для наследников, предоставляя им общую реализацию.

Пример:

abstract class Shape {
abstract area(): number;
describe(): void {
console.log("This is a shape.");
}
}
class Square extends Shape {
constructor(private side: number) {
super();
}
area(): number {
return this.side \* this.side;
}
}

Здесь абстрактный класс Shape задает обязательный метод area, но также предоставляет готовый метод describe, который наследники могут использовать без переопределения.

Ключевые различия

  1. **Наследование и реализация
    **

    • Интерфейс реализуется через implements. Класс может реализовать сразу несколько интерфейсов.

    • Абстрактный класс наследуется через extends. Класс может наследоваться только от одного абстрактного класса.

  2. **Наличие реализации
    **

    • Интерфейс никогда не содержит реализации. Он описывает только форму объекта.

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

  3. **Использование свойств
    **

    • Интерфейс может описывать только тип свойств и методов, но не их содержимое.

    • Абстрактный класс может содержать поля с конкретными значениями, которые будут доступны наследникам.

  4. **Модификаторы доступа
    **

    • В интерфейсах все свойства и методы по умолчанию публичные. Управлять их доступностью нельзя.

    • В абстрактных классах можно использовать public, protected и private, контролируя доступность свойств и методов.

  5. **Назначение
    **

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

    • Абстрактные классы применяются тогда, когда требуется общая реализация, которая будет разделяться между несколькими классами.

Когда выбирать интерфейс, а когда абстрактный класс

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

Таким образом, интерфейс — это чистый контракт без реализации, а абстрактный класс — это каркас с возможностью частичной или полной реализации отдельных элементов.