PurpleSchool — курсы программирования онлайн
  • Пути
    • Frontend React разработчик
    • Frontend Vue разработчик
    • Backend разработчик Node.js
    • Fullstack разработчик React / Node.js
    • Mobile разработчик React Native
    • Backend разработчик Golang
    • Devops инженер
    • Backend разработчик Python
  • Подписка
  • AI для кодаНовое
  • О нас
    • Отзывы
    • Реферальная программа
    • О компании
    • Контакты
  • Иконка открытия меню
    • PurpleFlow
    • Сообщество
    • PurpleПлюс
    • AI Собеседование
    • AI тренажёр
    • Проекты
PurpleSchool — платформа бесплатных roadmap и курсов для разработчиков
ютуб иконка
Telegram иконка
VK иконка
VK иконка
Курсы
ГлавнаяКаталог курсовFrontendBackendFullstack
Практика
КарьераПроектыPurpleПлюсPurpleMax
Материалы
БлогБаза знаний
Документы
Договор офертаПолитика конфиденциальностиПроверка сертификатаМиграция курсовРеферальная программа
Реквизиты
ИП Ларичев Антон АндреевичИНН 773373765379contact@purpleschool.ru

PurpleSchool © 2020 -2026 Все права защищены

  • Курсы
    • FrontendИконка стрелки
    • AI разработкаИконка стрелки
    • BackendИконка стрелки
    • DevOpsИконка стрелки
    • MobileИконка стрелки
    • ТестированиеИконка стрелки
    • Soft-skillsИконка стрелки
    • ДизайнИконка стрелки
    Иконка слояПерейти в каталог курсов
  • Бесплатно
    • Курсы
    • JavaScript Основы разработкиPython Основы PythonCSS CSS FlexboxКарта развитияВопросы для собеседований
    • База знанийИконка стрелки
    • Новостные рассылкиИконка стрелки
  • PurpleSchool — курсы программирования онлайн
    • Подписка
    • AI для кодаНовое
    • Сообщество
    Главная
    Сообщество
    SOLID принципы в JavaScript: разбор на примерах

    SOLID принципы в JavaScript: разбор на примерах

    Аватар автора SOLID принципы в JavaScript: разбор на примерах

    Антон Ларичев

    Иконка календаря26 июня 2026
    JavaScriptSOLIDООПархитектурапаттернычистый кодmiddleИконка уровня middle
    Картинка поста SOLID принципы в JavaScript: разбор на примерах

    Введение

    SOLID — это пять принципов объектно-ориентированного проектирования, которые помогают создавать код, устойчивый к изменениям. Аббревиатура расшифровывается: Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion. Несмотря на то что термины пришли из мира Java и C#, в JavaScript они применяются не менее активно — особенно в Node.js-приложениях и фронтенд-фреймворках.

    В этой статье мы разберём каждый принцип на конкретных примерах и покажем, как нарушение принципов приводит к проблемам на практике.

    S — Single Responsibility Principle (Принцип единственной ответственности)

    Класс или функция должны выполнять только одну задачу и иметь только одну причину для изменения.

    Нарушение принципа

    class UserService {
      // Класс делает слишком много: бизнес-логика, валидация и работа с БД вместе
      createUser(data) {
        if (!data.email.includes('@')) {
          throw new Error('Неверный email');
        }
        const user = { ...data, createdAt: new Date() };
        db.query('INSERT INTO users VALUES (?)', [user]);
        emailClient.send(data.email, 'Добро пожаловать!');
        return user;
      }
    }

    Соблюдение принципа

    class UserValidator {
      // Отвечает только за валидацию
      validate(data) {
        if (!data.email.includes('@')) {
          throw new Error('Неверный email');
        }
      }
    }
    
    class UserRepository {
      // Отвечает только за сохранение
      save(user) {
        return db.query('INSERT INTO users VALUES (?)', [user]);
      }
    }
    
    class UserService {
      constructor(validator, repository, mailer) {
        this.validator = validator;
        this.repository = repository;
        this.mailer = mailer;
      }
    
      createUser(data) {
        this.validator.validate(data);
        const user = { ...data, createdAt: new Date() };
        this.repository.save(user);
        this.mailer.sendWelcome(data.email);
        return user;
      }
    }

    Теперь каждый класс можно изменить и протестировать независимо.

    O — Open/Closed Principle (Принцип открытости/закрытости)

    Модуль должен быть открыт для расширения, но закрыт для модификации.

    // Плохо: при добавлении нового типа скидки придётся менять этот класс
    class DiscountService {
      calculate(user, price) {
        if (user.type === 'vip') return price * 0.8;
        if (user.type === 'student') return price * 0.9;
        return price;
      }
    }
    
    // Хорошо: новые скидки добавляются без изменения существующего кода
    class VipDiscount {
      apply(price) { return price * 0.8; }
    }
    
    class StudentDiscount {
      apply(price) { return price * 0.9; }
    }
    
    class NoDiscount {
      apply(price) { return price; }
    }
    
    class DiscountService {
      constructor(discountStrategy) {
        // Принимаем стратегию снаружи — расширяем через новые классы
        this.strategy = discountStrategy;
      }
    
      calculate(price) {
        return this.strategy.apply(price);
      }
    }

    L — Liskov Substitution Principle (Принцип подстановки Лисков)

    Дочерний класс должен полностью заменять родительский без изменения поведения программы.

    class Bird {
      fly() { return 'Лечу'; }
    }
    
    // Нарушение: пингвин — птица, но не умеет летать
    class Penguin extends Bird {
      fly() {
        throw new Error('Пингвины не летают');
      }
    }
    
    // Решение: выделить поведение в отдельные интерфейсы
    class Bird {
      move() { throw new Error('Метод не реализован'); }
    }
    
    class FlyingBird extends Bird {
      move() { return 'Лечу'; }
    }
    
    class SwimmingBird extends Bird {
      move() { return 'Плыву'; }
    }
    
    class Sparrow extends FlyingBird {}
    class Penguin extends SwimmingBird {}
    
    // Теперь любую птицу можно передать в функцию без неожиданных ошибок
    function makeMove(bird) {
      console.log(bird.move());
    }

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

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

    // Плохо: один большой объект со всеми методами
    class Animal {
      eat() {}
      sleep() {}
      fly() {}   // не все животные умеют летать
      swim() {}  // не все умеют плавать
    }
    
    // Хорошо: разбиваем на небольшие роли-миксины
    const canEat = (Base) => class extends Base {
      eat() { return `${this.name} ест`; }
    };
    
    const canFly = (Base) => class extends Base {
      fly() { return `${this.name} летит`; }
    };
    
    const canSwim = (Base) => class extends Base {
      swim() { return `${this.name} плывёт`; }
    };
    
    class Animal {
      constructor(name) { this.name = name; }
    }
    
    // Составляем только нужные возможности
    class Duck extends canEat(canFly(canSwim(Animal))) {}
    class Cat extends canEat(Animal) {}
    
    const duck = new Duck('Утка');
    console.log(duck.fly());  // Утка летит
    console.log(duck.swim()); // Утка плывёт

    D — Dependency Inversion Principle (Принцип инверсии зависимостей)

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

    // Плохо: высокоуровневый класс жёстко привязан к конкретной реализации
    class OrderService {
      constructor() {
        // Прямая зависимость от конкретного класса — не заменить на другой логгер
        this.logger = new FileLogger();
      }
    
      createOrder(data) {
        this.logger.log('Создан заказ');
        return data;
      }
    }
    
    // Хорошо: зависим от абстракции, конкретику передаём снаружи
    class OrderService {
      constructor(logger) {
        // Принимаем любой объект с методом log
        this.logger = logger;
      }
    
      createOrder(data) {
        this.logger.log('Создан заказ');
        return data;
      }
    }
    
    class FileLogger {
      log(msg) { fs.appendFileSync('app.log', msg); }
    }
    
    class ConsoleLogger {
      log(msg) { console.log(msg); }
    }
    
    // В продакшене используем файловый логгер, в тестах — консольный
    const service = new OrderService(new FileLogger());
    const testService = new OrderService(new ConsoleLogger());

    Частые ошибки

    Слишком формальное следование принципам. Создавать 10 классов там, где хватит одной функции — это оверинжиниринг. SOLID — инструмент, а не религия. Применяйте принципы там, где код реально будет меняться и расширяться.

    Путаница между SRP и дроблением. Единственная ответственность не означает «один метод на класс». Ответственность — это причина для изменения. Если класс хранит данные пользователя и умеет их сериализовать — у него всё ещё одна область изменений.

    Игнорирование LSP при работе с промисами. Если метод базового класса возвращает синхронное значение, а дочерний класс возвращает промис — это нарушение LSP. Держите контракты совместимыми.

    Dependency Injection без IoC-контейнера. Передавать зависимости через конструктор — это уже DI. Не нужно сразу внедрять тяжёлые фреймворки для маленьких проектов.

    Заключение

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

    Иконка глаза548

    Комментарии

    0
    Бесплатный курс

    Учитесь программировать с AI-агентом — бесплатно

    Курс «AI в обучении: OpenCode и skills»: поставите агента и разберёте с ним любую тему — как эту статью.

    opencode

    >объясни этот код и дай задачу

    ◆Читаю src/server.ts…

    ✓Разбор готов: 4 шага

    ✓Задача → practice/01.md

    • Установка и настройка OpenCode
    • Агент и skills на практике
    • 13 уроков, 3 модуля
    Начать бесплатно

    0 ₽ · доступ сразу после регистрации

    Бесплатные лекции

    Лучшие курсы по теме

    изображение курса

    Основы JavaScript

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    Продвинутый JavaScript

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    TypeScript с нуля

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее

    Похожие статьи

    Картинка поста Qwen Code Desktop: установка и подключение из России
    Иконка аватараАнтон
    Иконка календаря08 ноября 2026
    Qwen CodeAI-инструментыразработкаjuniorИконка уровня junior

    Qwen Code Desktop: установка и подключение из России

    Как скачать и установить Qwen Code Desktop на Windows, macOS и Linux и подключить модели Claude и Qwen из России без VPN: готовый settings.json и частые ошибки.

    Иконка чипа0
    Иконка глаза40
    Иконка комментариев0
    Картинка поста Верификация Claude из России: что это и почему не проходит
    Иконка аватараАнтон
    Иконка календаря07 ноября 2026
    AnthropicAI-инструментыClaude CodejuniorИконка уровня junior

    Верификация Claude из России: что это и почему не проходит

    Что такое верификация Claude через Persona, какие документы нужны, почему она не проходит и что делать из России без чужих документов и номеров.

    Иконка чипа0
    Иконка глаза717
    Иконка комментариев0
    Картинка поста Как установить OpenCode на Windows
    Иконка аватараАнтон
    Иконка календаря06 ноября 2026
    AI-инструментыapiразработкаjuniorИконка уровня junior

    Как установить OpenCode на Windows

    Как установить OpenCode на Windows через npm, Chocolatey, Scoop или WSL, задать ключ в PowerShell, подключить модель из России и исправить частые ошибки.

    Иконка чипа0
    Иконка глаза1 692
    Иконка комментариев0
    Иконка чипа0