← Назад к вопросам

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

2.0 Middle🔥 161 комментариев
#TypeScript#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между Абстрактным Классом и Интерфейсом

Для Frontend Developer с TypeScript это важная концепция для построения архитектуры. Абстрактные классы и интерфейсы служат разным целям, хотя обе используются для определения контрактов.

Интерфейс (Interface)

Interface — это контракт, который определяет структуру объекта (какие методы и свойства он должен иметь):

interface User {
  id: string;
  name: string;
  email: string;
  getFullName(): string;
}

// Любой класс может реализовать этот интерфейс
class AdminUser implements User {
  id = "1";
  name = "Admin";
  email = "admin@example.com";
  
  getFullName(): string {
    return this.name;
  }
}

class GuestUser implements User {
  id = "guest";
  name = "Guest";
  email = "guest@example.com";
  
  getFullName(): string {
    return "Guest User";
  }
}

Особенности интерфейса:

  • Только контракт — не может иметь реализацию методов
  • Несколько интерфейсов — класс может implements несколько интерфейсов
  • Стирается при компиляции — в JavaScript интерфейс существует только в TypeScript
  • Лёгкий — только структура, никакой логики
  • Для типизации — основная цель — типобезопасность

Абстрактный класс (Abstract Class)

Abstract class — это класс, который нельзя инстанцировать напрямую, но можно наследоваться:

abstract class Animal {
  // Свойства с реализацией
  protected name: string;
  protected age: number = 0;
  
  // Метод с реализацией
  constructor(name: string) {
    this.name = name;
  }
  
  // Конкретный метод
  sleep(): void {
    console.log(`${this.name} спит`);
  }
  
  // Абстрактный метод (без реализации)
  abstract makeSound(): void;
  abstract eat(): void;
}

class Dog extends Animal {
  makeSound(): void {
    console.log("Гав");
  }
  
  eat(): void {
    console.log("Ест мясо");
  }
}

class Cat extends Animal {
  makeSound(): void {
    console.log("Мяу");
  }
  
  eat(): void {
    console.log("Ест рыбу");
  }
}

// const animal = new Animal("Test"); // Ошибка!
const dog = new Dog("Шарик"); // Ок
dog.sleep();
dog.makeSound();

Особенности абстрактного класса:

  • Может иметь реализацию — методы могут быть конкретными
  • Может иметь свойства с инициализацией — защищённые данные
  • Один родитель — наследоваться можно только от одного класса
  • Существует в runtime — можно проверить instanceof
  • Для наследования — создание иерархии классов

Сравнение в таблице

АспектInterfaceAbstract Class
Реализация методовНет (только объявление)Может быть
СвойстваТолько типыС инициализацией
Множественное наследованиеНесколько implementsТолько один extends
InstancированиеНельзя (не класс)Нельзя (abstract)
Модификаторы доступаНетpublic, protected, private
В runtimeНет (стирается)Да
instanceofНельзяМожно
КонструкторНетДа

Практические различия

Interface — для контрактов и типов

// Интерфейс определяет, как должен выглядеть объект
interface Logger {
  log(message: string): void;
  warn(message: string): void;
  error(message: string): void;
}

// Разные реализации Logger
class ConsoleLogger implements Logger {
  log(message: string) {
    console.log(`[INFO] ${message}`);
  }
  warn(message: string) {
    console.warn(`[WARN] ${message}`);
  }
  error(message: string) {
    console.error(`[ERROR] ${message}`);
  }
}

class FileLogger implements Logger {
  log(message: string) {
    fs.writeFile('logs.txt', `[INFO] ${message}`);
  }
  warn(message: string) {
    fs.writeFile('logs.txt', `[WARN] ${message}`);
  }
  error(message: string) {
    fs.writeFile('logs.txt', `[ERROR] ${message}`);
  }
}

// Функция работает с любым Logger
function runApp(logger: Logger) {
  logger.log("App started");
}

Abstract Class — для иерархии и реиспользования кода

abstract class Repository {
  protected db: Database;
  
  constructor(db: Database) {
    this.db = db;
  }
  
  // Общая логика для всех репозиториев
  protected async executeQuery(sql: string) {
    console.log(`Executing: ${sql}`);
    return this.db.query(sql);
  }
  
  // Абстрактные методы, специфичные для каждого репозитория
  abstract async findById(id: string): Promise<any>;
  abstract async save(entity: any): Promise<void>;
}

class UserRepository extends Repository {
  async findById(id: string) {
    return this.executeQuery(`SELECT * FROM users WHERE id = '${id}'`);
  }
  
  async save(user: any) {
    return this.executeQuery(`INSERT INTO users VALUES (${user.id}, '${user.name}')`);
  }
}

class ProductRepository extends Repository {
  async findById(id: string) {
    return this.executeQuery(`SELECT * FROM products WHERE id = '${id}'`);
  }
  
  async save(product: any) {
    return this.executeQuery(`INSERT INTO products VALUES (${product.id}, '${product.name}')`);
  }
}

Когда использовать что

Interface используй когда:

  • Нужно определить структуру данных
  • Хочешь multiple "наследования" (несколько интерфейсов)
  • Фокус на типобезопасности и структуре
  • Нет общего кода для переиспользования
interface Comparable {
  compareTo(other: Comparable): number;
}

interface Drawable {
  draw(): void;
}

class Shape implements Comparable, Drawable {
  // ...
}

Abstract Class используй когда:

  • Нужна общая реализация для нескольких классов
  • Есть иерархия с parent-child отношением
  • Хочешь protected свойства и методы
  • Нужно управлять состоянием (конструктор)
abstract class Component {
  protected state: any;
  
  constructor() {
    this.state = {};
  }
  
  render(): void {
    console.log("Rendering:", this.state);
  }
  
  abstract update(): void;
}

Гибридный подход

В TypeScript часто используют оба вместе:

// Интерфейс для контракта
interface PaymentMethod {
  process(amount: number): Promise<boolean>;
}

// Абстрактный класс для общей логики
abstract class BasePaymentMethod implements PaymentMethod {
  protected logger: Logger;
  
  constructor(logger: Logger) {
    this.logger = logger;
  }
  
  async process(amount: number): Promise<boolean> {
    this.logger.log(`Processing ${amount}`);
    const result = await this.executePayment(amount);
    return result;
  }
  
  protected abstract executePayment(amount: number): Promise<boolean>;
}

class CreditCardPayment extends BasePaymentMethod {
  protected async executePayment(amount: number): Promise<boolean> {
    // Специфичная логика для карты
    return true;
  }
}

Вывод

Interface — это контракт ("ЧТО должен реализовать класс"), а Abstract Class — это базовая класс ("ЧТО и КАК должен сделать класс").

Выбирай interface для типобезопасности и контрактов, абстрактные классы для иерархии и переиспользования кода.

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