SOLID это аббревиатура, которая обозначает пять принципов объектно-ориентированного программирования. Эти принципы помогают разработчикам писать качественный и поддерживаемый код. Вот как выглядят эти принципы:

Single Responsibility Principle (Принцип единственной ответственности) - Каждый класс должен иметь одну ответственность (функционал) и все его методы должны быть связаны с этой ответственностью. Например:

class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }

  getName() {
    return this.name;
  }

  getEmail() {
    return this.email;
  }

  setEmail(email) {
    this.email = email;
  }
}

Open/Closed Principle (Принцип открытости/закрытости) - Классы должны быть открыты для расширения, но закрыты для модификации. Например:

class Shape {
  area() {
    throw new Error('Area method should be implemented');
  }
}

class Circle extends Shape {
  constructor(radius) {
    super();
    this.radius = radius;
  }

  area() {
    return Math.PI * this.radius * this.radius;
  }
}

class Rectangle extends Shape {
  constructor(width, height) {
    super();
    this.width = width;
    this.height = height;
  }

  area() {
    return this.width * this.height;
  }
}

В этом примере, мы создали абстрактный класс Shape, который содержит абстрактный метод area. Далее, мы реализовали этот метод в классах Circle и Rectangle, таким образом расширяя функциональность абстрактного класса Shape, но не меняя его код. Это демонстрирует принцип открытости/закрытости.

Liskov Substitution Principle (Принцип подстановки Барбары Лисков) - Объекты в программе должны быть заменяемыми на экземпляры их подтипов, не нарушая правильность программы. Например:

class Animal {
  eat() {}
  sleep() {}
  play() {}
}

class Cat {
  eat() {}
  sleep() {}
}

class Dog {
  eat() {}
  play() {}
}

В примере выше, класс Cat и Dog не используют метод play, но они все равно наследуют его от класса Animal, что нарушает принцип разделения интерфейса. Вместо этого, мы можем разделить интерфейс Animal на несколько меньших интерфейсов:

interface Eatable {
  eat();
}

interface Sleepable {
  sleep();
}

interface Playable {
  play();
}

class Cat implements Eatable, Sleepable {}

class Dog implements Eatable, Playable {}

Таким образом, класс Cat реализует только те методы, которые он действительно использует, а класс Dog реализует те методы, которые он использует.

И последний принцип: Dependency Inversion Principle (Принцип инверсии зависимостей) - Зависимости в программе должны быть направлены от абстракций к реализациям, а не наоборот. Например:

class User {
  constructor(database) {
    this.database = database;
  }

  save() {
    this.database.save();
  }
}

class MySQLDatabase {
  save() {
    console.log('Saving to MySQL database');
  }
}

const user = new User(new MySQLDatabase());
user.save(); // Output: 'Saving to MySQL database'

В примере выше, класс User не зависит от конкретной реализации класса MySQLDatabase, а зависит от абстракции Database. Это позволяет нам легко заменить реализацию базы данных на другую, не меняя код класса User.

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


  1. dlc
    08.01.2023 08:39
    +16

    В таких "объяснениях" всегда достаточно прочитать про SRP, увидеть очередной берд типа "один класс - одна ответственность" и скипнуть всё остальное.


  1. dopusteam
    08.01.2023 08:40
    +6

    Каждый класс должен иметь одну ответственность (функционал) и все его методы должны быть связаны с этой ответственностью

    Почему объект User является иллюстраций этого принципа? Что там такого необычного?

    В этом примере, мы создали абстрактный класс Shape, который содержит абстрактный метод area. Далее, мы реализовали этот метод в классах Circle и Rectangle, таким образом расширяя функциональность абстрактного класса Shape, но не меняя его код. Это демонстрирует принцип открытости/закрытости

    Т.е. open closed - это про наследование?

    В примере выше, класс Cat и Dog не используют метод play, но они все равно наследуют его от класса Animal, что нарушает принцип разделения интерфейса

    А при чем здесь принцип разделения интерфейсов, если раздел про принцип постановки Лисков?

    И где описание принципа разделения интерфейсов?

    Зависимости в программе должны быть направлены от абстракций к реализациям

    Т.е. абстракция должна зависеть от реализации? На примере js конечно тяжеловато этот принцип понять.

    Во всем этом не хватает примеров, а автору не хватает понимания, что такое solid принципы


    1. shark14
      08.01.2023 15:35

      Как можно на практике реализовать open-closed principle без наследования (и чтобы это не было костылем)? Мне правда интересно.

      В C++ есть шаблоны, ладно. Но предположим, мы используем Java или C# или подобные им enterprise-языки. Что бы вы сделали?


      1. panzerfaust
        08.01.2023 16:30

        Вы имеете ввиду наследование как выражения отношения is-a (которое extends в джаве) или subtyping в широком понимании (наследование классов, реализация интерфейсов)? Если subtyping вообще, то да, сразу так и не скажешь, как тут красиво OCP применить. Но если же нет, то ряд паттернов ООП как раз и строится на том, что наследованию предпочитается композиция и новый функционал добавляется без изменения старого. Это прокси, декоратор, стратегия.


        1. shark14
          08.01.2023 16:48

          Ну да, я имел в виду наследование не не обязательно наследование через extends в Java. Реализация интерфейса тоже ведь по сути является наследованием, только не реализации, а контракта. Однозначной эта терминология не является: https://softwareengineering.stackexchange.com/questions/316893/if-i-implement-an-interface-is-it-called-an-inheritance


      1. sshikov
        08.01.2023 18:15
        +1

        Ну очевидный вариант — передача функций, как сущностей первого класса. Функции пишутся тем, кто расширяет. Это не стопроцентно универсальный вариант, но ни разу и не костыль.


        1. shark14
          08.01.2023 18:46
          +1

          Да, справедливо и это действительно не костыль. Спасибо!


  1. Hrodvitnir
    08.01.2023 08:59
    +4

    22-ой год, SOLID'у двадцать лет, а на Хабре нам все еще объясняют его простыми словами


    1. s_f1
      08.01.2023 12:19
      -2

      С одной стороны – да, с другой – существует же

      image


    1. panzerfaust
      08.01.2023 12:38
      -2

      Увы, SOLID так и не стал дефолтной философией разработки даже спустя 20 лет. По-прежнему доминирующий подход это методика Скарлетт О'Хары: "хуяк хуяк и в продакшн, а о проблемах я подумаю завтра". Поэтому просвещение все еще необходимо, но не в таких жалких формах.


      1. Hrodvitnir
        08.01.2023 12:52
        +1

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


      1. sshikov
        08.01.2023 18:13
        +1

        >дефолтной философией разработки
        А он должен был? Во-первых, это философия создания расширяемых и легко сопровождаемых программ. Я соглашусь, что такое требование достаточно типично — но не на 100%, и это не главный критерий качества софта, я бы сказал — почти никогда не главный. Кроме того, софт, написанный без учета SOLID, вполне может быть и так легко сопровождаемым и расширяемым — потому что есть и другие способы достижения того же эффекта. В третьих, это все в основном применимо только к ООП, на котором мир нынче не заканчивается, а скорее только начинается (то есть, снова есть другие способы достижения результата). Наконец, это все не бесплатно (именно отсюда ноги растут у вашего «а о проблемах я подумаю завтра»), а сроки и стоимость разработки — как раз зачастую второе требование к софту, сразу после функциональности.

        >просвещение все еще необходимо
        Знаете, вот на мой взгляд, весь SOLID сводится к одной простой формуле — ограничивайте распространение изменений по кодовой базе. Не давайте простому изменению затронуть кучу файлов, модулей, и т.п. И все принципы SOLID — они просто о частных случаях этого правила, как сделать, чтобы не было лишних зависимостей, которые потянут за собой изменения в таких местах, где они вообще не нужны. И это по большому счету все. Вы правда думаете, что тут нужно какое-то отдельное просвещение (насчет жалкой формы совершенно согласен)?


        1. panzerfaust
          08.01.2023 21:21

          весь SOLID сводится к одной простой формуле ... И это по большому счету все. Вы правда думаете, что тут нужно какое-то отдельное просвещение

          Ну знаете. Для кого-то и тригонометрия это "по большому счету" пара базовых формул, из которых выводятся все остальные. А для кого-то это сложная тема, которую разжевывают аж с 8 класса и продолжают в вузе. И вот когда нарешал пару тысяч примеров, то понимаешь, что все довольно элементарно. Чем навык писать по SOLID принципиально хуже, чем любой другой навык, что ему не нужно просвещать?

          а сроки и стоимость разработки — как раз зачастую второе требование к софту, сразу после функциональности

          Я еще не видел ни одного внятного доказательства, что писать по SOLID как-то принципиально дольше и сложнее, чем писать по "хуяк и в продакшн". Опять же вопрос практики. Если знаешь, что делать, - сходу пишешь быстро. Рефакторинг спагетти после бессистемного кодинга - это тоже как бы "сроки и стоимость", за которые заплатит бизнес.


          1. sshikov
            08.01.2023 21:35
            +2

            >Ну знаете.
            Ну я немного специально утрирую возможно. Но давайте обсудим. Что существенного я упустил? Если реально понимать, для чего принципы нужны — так ли уж нужно разжевывать каждый из них в отдельности? Ведь по сути, каждый из них говорит об одном — делайте так, чтобы (нужные) изменения не распространялись по коду куда попало (от наследников к родительским классам, от реализаций к интерфейсам и т.п.). По сути, это базовые принципы борьбы со сложностью. В такой постановке в них самих нет вообще ничего сложного.

            >Опять же вопрос практики.
            Я согласен — при ее наличии многие принципы соблюдаются сами собой. Но практика тоже не дается даром. В общем, я не настаиваю, я скорее так, обсудить хотел.


            1. panzerfaust
              09.01.2023 07:02
              +1

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

              Вся "наука" бокса говорит об одном: бей соперника, пока он не упадет, а сам удары не пропускай. Вам достаточно этой простой мысли, чтобы выйти на ринг? Или лучше сначала "разжевать" с тренером?

              так ли уж нужно разжевывать каждый из них в отдельности?

              Я сейчас менторю джуна, который, естественно, пишет дикий говнокод и нарушает SOLID на каждом шагу. Как я должен поступить по вашему? Просто сказать "пиши хорошо, плохо не пиши"? Нет, я разжевываю с ним каждый кейс. Что именно тут нарушается, почему это плохо, как сделать лучше. Эту передачу опыта я и имею в виду под просвещением. Я не в курсе, почему кто-то из минусующих и дискутирующих решил, что я предлагаю писать на хабре жалкие статьи а ля "что такое SRP на примере Delphi"


              1. sshikov
                09.01.2023 18:48

                >дискутирующих решил, что я предлагаю писать на хабре жалкие статьи а ля «что такое SRP на примере Delphi»
                Ну не, я так точно не решил (тем более это явно было написано в вашем первом комменте данной ветки). Насчет вашего джуна, ринга и прочего (я-то сам скорее тренер/инструктор по спортивным играм и горным лыжам, но пофиг) — есть разные люди, кому-то нужно понять принцип, а детали техники он сам построит и адаптирует, под свои особенности. А кому-то нужно разжевать детали, и он будет выполнять движение ровно так, как ему показали, и не будет адаптироваться сам. Не исключаю, что будучи сам из первой группы, я сложности второй группы недооцениваю. Даже наверняка.


  1. enkryptor
    08.01.2023 12:44
    +3

    После того, как начинающий программист прочитал краткий пересказ Мартина, ему может показаться, что он понял, что такое SOLID, и что он даже может пересказать это своими словами.

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

    Через год-полтора такого карго-культа к нему приходит понимание, что он делает что-то не так, но как это поправить, ему всё ещё неясно. В особо упоротых случаях это превращается в новую статью на хабре вида "SOLID надо знать только для собеседований, на практике он не работает".

    Подобные ещё более короткие пересказы только усугубляют такую ситуацию. Про один только SRP можно закатить лекцию часика на три, и всё равно суть может остаться не ясна без конкретной практики. А пересказ в двух словах оставляет ложное ощущение понимания, которое затем только вредит и замедляет прогресс программиста.


    1. Hrodvitnir
      08.01.2023 12:55

      Если у тебя проект лендоса, без сложной логики, то никакой SOLID там и не нужен
      SOLID необходим там, где у тебя тысячи файлов и десятки, если не сотни модулей
      И то, например писать под каждый класс интерфейс -- та еще затея

      Так что SOLID, как и все в этом мире надо сначало хорошо понять, а потом понять куда это можно и нужно запихнуть


  1. itGuevara
    08.01.2023 14:00

    Принцнипы SOLID 

    Принципы видимо.


  1. vlad1988_1
    08.01.2023 17:24

    Похоже автор даже не вычитывал текст.

    Под пунктом Liskov Substitution Principle автор небрежно преподносит Interface segregation principle.

    Явно нет серьезного куска текста. Тот случай когда Ctrl+C - Ctrl+V не дают ни знаний ни новой информации другим, а скорее вредят.