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

Применял ли SOLID в JS

1.6 Junior🔥 222 комментариев
#JavaScript Core#Архитектура и паттерны

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

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

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

Применение SOLID принципов в JavaScript

Да, я активно применяю SOLID принципы в JavaScript-разработке. Несмотря на то, что SOLID изначально был сформулирован для объектно-ориентированных языков, эти принципы прекрасно адаптируются к JavaScript благодаря его гибкой природе, особенно с появлением ES6 классов и TypeScript. SOLID — это не строгие правила, а руководства для создания поддерживаемого, масштабируемого и тестируемого кода.

Как каждый принцип применяется в JavaScript

1. Принцип единственной ответственности (Single Responsibility)

Каждый класс или модуль должен иметь только одну причину для изменения. В JavaScript это особенно важно из-за динамической типизации.

Пример нарушения:

// ПЛОХО: Класс выполняет несколько задач
class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }
  
  saveToDatabase() {
    // логика сохранения в БД
  }
  
  sendEmail() {
    // логика отправки email
  }
  
  validate() {
    // логика валидации
  }
}

Пример соблюдения:

// ХОРОШО: Разделение ответственности
class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }
}

class UserRepository {
  save(user) {
    // логика сохранения в БД
  }
}

class EmailService {
  sendWelcomeEmail(user) {
    // логика отправки email
  }
}

class UserValidator {
  validate(user) {
    // логика валидации
  }
}

2. Принцип открытости/закрытости (Open/Closed)

Сущности должны быть открыты для расширения, но закрыты для модификации. В JavaScript это достигается через композицию и стратегию.

Пример:

// Базовый класс, закрытый для модификации
class Discount {
  calculate(amount) {
    return amount;
  }
}

// Расширение через наследование
class SeasonalDiscount extends Discount {
  calculate(amount) {
    return amount * 0.9; // 10% скидка
  }
}

class VIPDiscount extends Discount {
  calculate(amount) {
    return amount * 0.8; // 20% скидка
  }
}

// Использование
const discounts = {
  regular: new Discount(),
  seasonal: new SeasonalDiscount(),
  vip: new VIPDiscount()
};

function calculateTotal(amount, discountType) {
  return discounts[discountType].calculate(amount);
}

3. Принцип подстановки Лисков (Liskov Substitution)

Объекты должны быть заменяемы экземплярами их подтипов без изменения корректности программы.

Пример нарушения (типичная ошибка в JS):

class Rectangle {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }
  
  setWidth(width) {
    this.width = width;
  }
  
  setHeight(height) {
    this.height = height;
  }
  
  area() {
    return this.width * this.height;
  }
}

class Square extends Rectangle {
  setWidth(width) {
    this.width = width;
    this.height = width; // Нарушает поведение родителя
  }
  
  setHeight(height) {
    this.height = height;
    this.width = height; // Нарушает поведение родителя
  }
}

4. Принцип разделения интерфейса (Interface Segregation)

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

Пример:

// Вместо одного огромного класса
class Worker {
  work() {}
  eat() {}
  sleep() {}
}

// Создаем специализированные классы
class Workable {
  work() {
    console.log('Working...');
  }
}

class Eatable {
  eat() {
    console.log('Eating...');
  }
}

// Композиция вместо наследования
class HumanWorker {
  constructor() {
    this.workable = new Workable();
    this.eatable = new Eatable();
  }
  
  work() {
    this.workable.work();
  }
  
  eat() {
    this.eatable.eat();
  }
}

class RobotWorker {
  constructor() {
    this.workable = new Workable();
    // Роботу не нужно есть
  }
  
  work() {
    this.workable.work();
  }
}

5. Принцип инверсии зависимостей (Dependency Inversion)

Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций.

Пример с внедрением зависимостей:

// Абстракция (в JS часто используется в виде контракта)
class PaymentProcessor {
  process(amount) {
    throw new Error('Method not implemented');
  }
}

// Конкретная реализация
class StripeProcessor extends PaymentProcessor {
  process(amount) {
    console.log(`Processing $${amount} via Stripe`);
    // Логика Stripe
  }
}

class PayPalProcessor extends PaymentProcessor {
  process(amount) {
    console.log(`Processing $${amount} via PayPal`);
    // Логика PayPal
  }
}

// Высокоуровневый модуль зависит от абстракции
class OrderService {
  constructor(paymentProcessor) {
    this.paymentProcessor = paymentProcessor;
  }
  
  checkout(amount) {
    this.paymentProcessor.process(amount);
  }
}

// Использование
const order1 = new OrderService(new StripeProcessor());
order1.checkout(100);

const order2 = new OrderService(new PayPalProcessor());
order2.checkout(200);

Практические преимущества применения SOLID в JavaScript

  1. Улучшенная тестируемость — Изолированные модули легко тестировать с помощью Jest, Mocha и других фреймворков
  2. Упрощенное рефакторинг — Изменения в одной части системы минимально затрагивают другие части
  3. Повышение переиспользования кода — Маленькие, специализированные модули легче комбинировать
  4. Улучшенная читаемость — Код следует предсказуемым паттернам
  5. Облегчение командной работы — Разные разработчики могут работать над разными модулями с минимальными конфликтами

Особенности применения в экосистеме JavaScript

  • В React/Vue компонентах — Принцип единственной ответственности помогает создавать переиспользуемые компоненты
  • В Node.js приложениях — Принцип инверсии зависимостей упрощает конфигурирование и тестирование
  • В архитектуре Flux/Redux — Разделение ответственности между action creators, reducers и компонентами
  • С TypeScript — Интерфейсы и абстрактные классы делают применение SOLID более явным и контролируемым

Важный нюанс: В JavaScript SOLID применяется более гибко, чем в строго типизированных ООП языках. Часто используется композиция вместо наследования, функциональные подходы вместе с ООП, и принципы адаптируются под конкретные фреймворки и библиотеки.

На практике я сочетаю SOLID с другими принципами (DRY, KISS, YAGNI) и паттернами проектирования, создавая архитектуру, которая остается понятной и поддерживаемой даже по мере роста сложности приложения.

Применял ли SOLID в JS | PrepBro