0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p><em>Примечание - Это адаптированный перевод<a>статьи Роберта Мартина Solid Relevance</a>. Повествование ведётся от лица автора оригинала.</em></p>
1
<p><em>Примечание - Это адаптированный перевод<a>статьи Роберта Мартина Solid Relevance</a>. Повествование ведётся от лица автора оригинала.</em></p>
2
<p>Недавно я получил письмо, автор которого интересовался релевантностью принципов SOLID. Вот что он написал:</p>
2
<p>Недавно я получил письмо, автор которого интересовался релевантностью принципов SOLID. Вот что он написал:</p>
3
<blockquote><p>Многие годы вопросы о принципах SOLID были стандартными на собеседованиях в нашей компании. Мы ожидали от кандидатов хорошего понимания этих принципов. В какой-то момент один из наших менеджеров, который не полностью погружён в программирование, спросил, актуально ли говорить с кандидатами о SOLID. Он сказал, что принцип открытости-закрытости стал не таким важным, как раньше. Это связано с переходом от монолитов к микросервисной архитектуре. Принцип подстановки Лисков давно устарел, потому что мы уже не уделяем столько же внимания наследованию, как 20 лет назад. Думаю, нам стоит учитывать<a>позицию Дэна Норта в отношении SOLID</a>, которую он выразил в рекомендации: "Пишите простой код".</p>
3
<blockquote><p>Многие годы вопросы о принципах SOLID были стандартными на собеседованиях в нашей компании. Мы ожидали от кандидатов хорошего понимания этих принципов. В какой-то момент один из наших менеджеров, который не полностью погружён в программирование, спросил, актуально ли говорить с кандидатами о SOLID. Он сказал, что принцип открытости-закрытости стал не таким важным, как раньше. Это связано с переходом от монолитов к микросервисной архитектуре. Принцип подстановки Лисков давно устарел, потому что мы уже не уделяем столько же внимания наследованию, как 20 лет назад. Думаю, нам стоит учитывать<a>позицию Дэна Норта в отношении SOLID</a>, которую он выразил в рекомендации: "Пишите простой код".</p>
4
</blockquote><p>В ответном письме я написал следующее.</p>
4
</blockquote><p>В ответном письме я написал следующее.</p>
5
<p>Сегодня принципы SOLID остаются такими же релевантными, как в 90-е годы и раньше. Это связано с тем, что программы практически не изменились за эти годы. Более того, программы сильно не изменились с 1945 года, когда Алан Тьюринг написал первые строки кода для электронного компьютера. Программы всё ещё состоят из операторов if, циклов while и операторов присваивания. Это всё ещё последовательность, перебор и итерация.</p>
5
<p>Сегодня принципы SOLID остаются такими же релевантными, как в 90-е годы и раньше. Это связано с тем, что программы практически не изменились за эти годы. Более того, программы сильно не изменились с 1945 года, когда Алан Тьюринг написал первые строки кода для электронного компьютера. Программы всё ещё состоят из операторов if, циклов while и операторов присваивания. Это всё ещё последовательность, перебор и итерация.</p>
6
<p>Каждому новому поколению нравится думать, что его мир сильно отличается от мира, в котором жили предыдущие поколения. Это ошибка, которую совершает каждое поколение. Оно осознаёт эту ошибку, когда появляется следующее поколение, которое приходит и рассказывает, как сильно всё вокруг изменилось.</p>
6
<p>Каждому новому поколению нравится думать, что его мир сильно отличается от мира, в котором жили предыдущие поколения. Это ошибка, которую совершает каждое поколение. Оно осознаёт эту ошибку, когда появляется следующее поколение, которое приходит и рассказывает, как сильно всё вокруг изменилось.</p>
7
<p>Давайте рассмотрим каждый принцип и оценим его актуальность.</p>
7
<p>Давайте рассмотрим каждый принцип и оценим его актуальность.</p>
8
<h2>Содержание</h2>
8
<h2>Содержание</h2>
9
<ul><li><a>Принцип единственной ответственности</a></li>
9
<ul><li><a>Принцип единственной ответственности</a></li>
10
<li><a>Принцип открытости-закрытости</a></li>
10
<li><a>Принцип открытости-закрытости</a></li>
11
<li><a>Принцип подстановки Лисков</a></li>
11
<li><a>Принцип подстановки Лисков</a></li>
12
<li><a>Принцип разделения интерфейса</a></li>
12
<li><a>Принцип разделения интерфейса</a></li>
13
<li><a>Принцип инверсии зависимостей</a></li>
13
<li><a>Принцип инверсии зависимостей</a></li>
14
</ul><h2>Принцип единственной ответственности</h2>
14
</ul><h2>Принцип единственной ответственности</h2>
15
<blockquote><p>Держите вместе сущности, которые меняются по одной причине. Разделяйте сущности, которые меняются по разным причинам.</p>
15
<blockquote><p>Держите вместе сущности, которые меняются по одной причине. Разделяйте сущности, которые меняются по разным причинам.</p>
16
</blockquote><p>Сложно представить, что этот принцип стал нерелевантным. Мы не смешиваем код, который отвечает за бизнес-логику, с кодом, который отвечает за интерфейсы. Мы не смешиваем SQL-запросы с протоколами передачи данных. Мы разделяем код, который меняется по разным причинам, благодаря чему изменения в одной подсистеме не влияют на другие подсистемы. Мы следим, чтобы модули, которые меняются по разным причинам, не влияли друг на друга.</p>
16
</blockquote><p>Сложно представить, что этот принцип стал нерелевантным. Мы не смешиваем код, который отвечает за бизнес-логику, с кодом, который отвечает за интерфейсы. Мы не смешиваем SQL-запросы с протоколами передачи данных. Мы разделяем код, который меняется по разным причинам, благодаря чему изменения в одной подсистеме не влияют на другие подсистемы. Мы следим, чтобы модули, которые меняются по разным причинам, не влияли друг на друга.</p>
17
<p>Микросервисы не решают эту проблему. Если вы смешиваете код, который меняется по разным причинам, то можете получить запутанные микросервисы или даже наборы запутанных микросервисов.</p>
17
<p>Микросервисы не решают эту проблему. Если вы смешиваете код, который меняется по разным причинам, то можете получить запутанные микросервисы или даже наборы запутанных микросервисов.</p>
18
<p>Слайды Дэна Норта, ссылка на которые приводится выше, не учитывают этого момента. Из-за этого мне кажется, что он вообще не понимает принцип единственной ответственности, или что он иронизировал. Насколько я знаю Дэна, последнее очень вероятно. В ответ на упоминание принципа единственной ответственности он предлагает писать простой код. Я согласен с этим. Принцип единственной ответственности - один из способов писать простой код.</p>
18
<p>Слайды Дэна Норта, ссылка на которые приводится выше, не учитывают этого момента. Из-за этого мне кажется, что он вообще не понимает принцип единственной ответственности, или что он иронизировал. Насколько я знаю Дэна, последнее очень вероятно. В ответ на упоминание принципа единственной ответственности он предлагает писать простой код. Я согласен с этим. Принцип единственной ответственности - один из способов писать простой код.</p>
19
<h2>Принцип открытости-закрытости</h2>
19
<h2>Принцип открытости-закрытости</h2>
20
<blockquote><p>Модуль должен быть открыт для расширения, но закрыт для модификации.</p>
20
<blockquote><p>Модуль должен быть открыт для расширения, но закрыт для модификации.</p>
21
</blockquote><p>Когда кто-то сомневается в релевантности именно этого принципа, я сильнее всего беспокоюсь за будущее программирования. Конечно, мы хотим создавать модули, которые можно расширять без модификации. Вы можете представить себе работу с системой, в которой не реализована независимость от типа девайса, например, в которой запись файла на диск фундаментально отличается от печати файла на принтере? Мы же не хотим видеть отдельные инструкции для каждого типа девайса в коде?</p>
21
</blockquote><p>Когда кто-то сомневается в релевантности именно этого принципа, я сильнее всего беспокоюсь за будущее программирования. Конечно, мы хотим создавать модули, которые можно расширять без модификации. Вы можете представить себе работу с системой, в которой не реализована независимость от типа девайса, например, в которой запись файла на диск фундаментально отличается от печати файла на принтере? Мы же не хотим видеть отдельные инструкции для каждого типа девайса в коде?</p>
22
<p>Или… Хотим ли мы отделять абстрактные концепции от конкретных понятий? Хотим ли мы отделять бизнес-логику от небольших деталей, связанных с интерфейсами, или от протоколов передачи данных, или произвольного поведения базы данных? Конечно да!</p>
22
<p>Или… Хотим ли мы отделять абстрактные концепции от конкретных понятий? Хотим ли мы отделять бизнес-логику от небольших деталей, связанных с интерфейсами, или от протоколов передачи данных, или произвольного поведения базы данных? Конечно да!</p>
23
<p>И снова в слайдах Дэна Норта есть ошибка. При изменении требований только часть существующего кода становится неактуальной. Но значительная часть кода остаётся актуальной. И мы хотим знать, что нам не придётся менять работающую часть кода только для того, чтобы неработающий код снова заработал. Дэн снова предлагает писать простой код. И я снова соглашаюсь. Соблюдение принципа открытости-закрытости помогает писать простой код.</p>
23
<p>И снова в слайдах Дэна Норта есть ошибка. При изменении требований только часть существующего кода становится неактуальной. Но значительная часть кода остаётся актуальной. И мы хотим знать, что нам не придётся менять работающую часть кода только для того, чтобы неработающий код снова заработал. Дэн снова предлагает писать простой код. И я снова соглашаюсь. Соблюдение принципа открытости-закрытости помогает писать простой код.</p>
24
<h2>Принцип подстановки Лисков</h2>
24
<h2>Принцип подстановки Лисков</h2>
25
<blockquote><p>Если программа использует интерфейс, она не должна знать о реализации этого интерфейса.</p>
25
<blockquote><p>Если программа использует интерфейс, она не должна знать о реализации этого интерфейса.</p>
26
</blockquote><p>Люди, и я в том числе, долгое время ошибались, думая, что принцип подстановки Лисков применим только к наследованию. Это не так. Этот принцип определяет использование подтипов.</p>
26
</blockquote><p>Люди, и я в том числе, долгое время ошибались, думая, что принцип подстановки Лисков применим только к наследованию. Это не так. Этот принцип определяет использование подтипов.</p>
27
<p>Все реализации интерфейса являются подтипами этого интерфейса. Все утиные типы относятся к подтипам подразумеваемого интерфейса. А каждый пользователь базового интерфейса должен принимать значение этого интерфейса. Если реализация мешает пользователю базового типа, количество конструкций if/switch в коде растёт.</p>
27
<p>Все реализации интерфейса являются подтипами этого интерфейса. Все утиные типы относятся к подтипам подразумеваемого интерфейса. А каждый пользователь базового интерфейса должен принимать значение этого интерфейса. Если реализация мешает пользователю базового типа, количество конструкций if/switch в коде растёт.</p>
28
<p>Принцип подстановки Лисков говорит о необходимости чётко определять абстракции. Невозможно поверить, что этот принцип устарел.</p>
28
<p>Принцип подстановки Лисков говорит о необходимости чётко определять абстракции. Невозможно поверить, что этот принцип устарел.</p>
29
<p>В этом случае Дэн и его слайды правы. Он просто упустил из виду детали: простой код - это код, в котором чётко выделены абстракции.</p>
29
<p>В этом случае Дэн и его слайды правы. Он просто упустил из виду детали: простой код - это код, в котором чётко выделены абстракции.</p>
30
<h2>Принцип разделения интерфейса</h2>
30
<h2>Принцип разделения интерфейса</h2>
31
<blockquote><p>Делайте интерфейсы маленькими, чтобы клиенты не зависели от вещей, которыми они не пользуются.</p>
31
<blockquote><p>Делайте интерфейсы маленькими, чтобы клиенты не зависели от вещей, которыми они не пользуются.</p>
32
</blockquote><p>Мы всё ещё работаем<a>с компилируемыми языками</a>. Мы всё ещё зависим от дат модификации, с помощью которых определяем, какие модули нужно перекомпилировать и повторно задеплоить. Пока это так, придётся сталкиваться с проблемой зависимости модуля A от модуля B во время компиляции, а не во время выполнения, поскольку изменения в модуле B приведут к перекомпиляции и повторному деплою модуля A.</p>
32
</blockquote><p>Мы всё ещё работаем<a>с компилируемыми языками</a>. Мы всё ещё зависим от дат модификации, с помощью которых определяем, какие модули нужно перекомпилировать и повторно задеплоить. Пока это так, придётся сталкиваться с проблемой зависимости модуля A от модуля B во время компиляции, а не во время выполнения, поскольку изменения в модуле B приведут к перекомпиляции и повторному деплою модуля A.</p>
33
<p>Эта проблема стоит особенно остро в языках со статической типизацией, например, Java, C#, C++, Go, Swift и так далее. Языки с динамической типизацией подвержены этому в гораздо меньшей степени, но всё-таки полностью не защищены от этой проблемы. Это доказывает существование таких инструментов, как Maven и Leiningen.</p>
33
<p>Эта проблема стоит особенно остро в языках со статической типизацией, например, Java, C#, C++, Go, Swift и так далее. Языки с динамической типизацией подвержены этому в гораздо меньшей степени, но всё-таки полностью не защищены от этой проблемы. Это доказывает существование таких инструментов, как Maven и Leiningen.</p>
34
<p>Слайд Дэна, посвящённый этому принципу, содержит явно неправильную информацию. Клиенты однозначно зависят от методов, которые не используют, если они нуждаются в перекомпиляции и повторном деплое, когда эти методы меняются. Финальное замечание Дэна здравое, насколько это возможно. Да, если вы можете разделить класс с двумя интерфейсами на два класса, сделайте это. Это соответствует принципу единой ответственности. Но такое разделение часто невозможно и даже нежелательно.</p>
34
<p>Слайд Дэна, посвящённый этому принципу, содержит явно неправильную информацию. Клиенты однозначно зависят от методов, которые не используют, если они нуждаются в перекомпиляции и повторном деплое, когда эти методы меняются. Финальное замечание Дэна здравое, насколько это возможно. Да, если вы можете разделить класс с двумя интерфейсами на два класса, сделайте это. Это соответствует принципу единой ответственности. Но такое разделение часто невозможно и даже нежелательно.</p>
35
<h2>Принцип инверсии зависимостей</h2>
35
<h2>Принцип инверсии зависимостей</h2>
36
<blockquote><p>Модули верхнего уровня не должны зависеть от деталей реализации модулей нижнего уровня.</p>
36
<blockquote><p>Модули верхнего уровня не должны зависеть от деталей реализации модулей нижнего уровня.</p>
37
</blockquote><p>Трудно представить архитектуру, в которой не используется этот принцип. Мы не хотим, чтобы высокоуровневая бизнес-логика зависела от деталей реализации на нижних уровнях. Я надеюсь, что это очевидно. Мы не хотим, чтобы вычисления, которые приносят нам деньги, смешивались с SQL-запросами, низкоуровневой валидацией или форматированием.</p>
37
</blockquote><p>Трудно представить архитектуру, в которой не используется этот принцип. Мы не хотим, чтобы высокоуровневая бизнес-логика зависела от деталей реализации на нижних уровнях. Я надеюсь, что это очевидно. Мы не хотим, чтобы вычисления, которые приносят нам деньги, смешивались с SQL-запросами, низкоуровневой валидацией или форматированием.</p>
38
<p>Мы хотим изолировать высокоуровневые абстракции от низкоуровневых деталей. Это разделение возможно благодаря корректному управлению зависимостями внутри системы. Это управление позволяет всем зависимостям в исходном коде, особенно пересекающим архитектурные границы, указывать на высокоуровневые абстракции, а не на низкоуровневые детали.</p>
38
<p>Мы хотим изолировать высокоуровневые абстракции от низкоуровневых деталей. Это разделение возможно благодаря корректному управлению зависимостями внутри системы. Это управление позволяет всем зависимостям в исходном коде, особенно пересекающим архитектурные границы, указывать на высокоуровневые абстракции, а не на низкоуровневые детали.</p>
39
<p>Слайды Дэна каждый раз заканчиваются призывом писать простой код. Это хороший совет. Но если годы развития индустрии чему-то нас научили, так это тому, что простота требует дисциплины, которая возможна благодаря принципам. Это те самые принципы, которые определяют простоту. Это та дисциплина, которая заставляет программистов писать простой код.</p>
39
<p>Слайды Дэна каждый раз заканчиваются призывом писать простой код. Это хороший совет. Но если годы развития индустрии чему-то нас научили, так это тому, что простота требует дисциплины, которая возможна благодаря принципам. Это те самые принципы, которые определяют простоту. Это та дисциплина, которая заставляет программистов писать простой код.</p>
40
<p>Лучший способ всё усложнить и устроить беспорядок - просто посоветовать людям писать простой код и ничего больше им не объяснить.</p>
40
<p>Лучший способ всё усложнить и устроить беспорядок - просто посоветовать людям писать простой код и ничего больше им не объяснить.</p>