0 added
0 removed
Original
2026-01-01
Modified
2026-02-21
1
<p><a>#статьи</a></p>
1
<p><a>#статьи</a></p>
2
<ul><li>7 сен 2021</li>
2
<ul><li>7 сен 2021</li>
3
<li>0</li>
3
<li>0</li>
4
</ul><p>Они взорвали мой мозг, и я мгновенно стал программировать лучше.</p>
4
</ul><p>Они взорвали мой мозг, и я мгновенно стал программировать лучше.</p>
5
<p>zaidi razak / Shutterstock</p>
5
<p>zaidi razak / Shutterstock</p>
6
<p>Фулстек-разработчик. Любимый стек: Java + Angular, но в хорошей компании готова писать хоть на языке Ада.</p>
6
<p>Фулстек-разработчик. Любимый стек: Java + Angular, но в хорошей компании готова писать хоть на языке Ада.</p>
7
<p><strong><strong>об авторе</strong></strong></p>
7
<p><strong><strong>об авторе</strong></strong></p>
8
<p>Разработчик ПО. Увлечён искусственным интеллектом и компьютерным зрением.</p>
8
<p>Разработчик ПО. Увлечён искусственным интеллектом и компьютерным зрением.</p>
9
<p>Вы слышали про объектную гимнастику (Object Calisthenics)? Если нет, то вам точно стоит её освоить! Тем более что это всего-то девять правил.</p>
9
<p>Вы слышали про объектную гимнастику (Object Calisthenics)? Если нет, то вам точно стоит её освоить! Тем более что это всего-то девять правил.</p>
10
<p>Я рассказывал о них многим разработчикам, и все они реагировали схоже. Сначала думали, что это шутка: "Как вообще можно хоть какой-то код написать по этим правилам?" Но, поприменяв их, обычно соглашались, что код и правда становится чище - причём почти мгновенно.</p>
10
<p>Я рассказывал о них многим разработчикам, и все они реагировали схоже. Сначала думали, что это шутка: "Как вообще можно хоть какой-то код написать по этим правилам?" Но, поприменяв их, обычно соглашались, что код и правда становится чище - причём почти мгновенно.</p>
11
<p>Понятие "объектная гимнастика" ввёл Джефф Бэй (Jeff Bay) в своей книге "The ThoughtWorks Anthology" (на русском не издавалась. - Пер.). Так он назвал группу упражнений на объектно-ориентированное программирование.</p>
11
<p>Понятие "объектная гимнастика" ввёл Джефф Бэй (Jeff Bay) в своей книге "The ThoughtWorks Anthology" (на русском не издавалась. - Пер.). Так он назвал группу упражнений на объектно-ориентированное программирование.</p>
12
<p>Применяя объектную гимнастику, можно сделать код:</p>
12
<p>Применяя объектную гимнастику, можно сделать код:</p>
13
<ul><li>читабельнее;</li>
13
<ul><li>читабельнее;</li>
14
<li>удобнее:</li>
14
<li>удобнее:</li>
15
</ul><p> • для отладки,</p>
15
</ul><p> • для отладки,</p>
16
<p> • тестирования,</p>
16
<p> • тестирования,</p>
17
<p> • повторного использования</p>
17
<p> • повторного использования</p>
18
<p> • и сопровождения.</p>
18
<p> • и сопровождения.</p>
19
<p>Считайте это упражнением. Вам нужно научиться выполнять эти правила при кодинге. А вот в повседневном программировании какими-то из них всегда можно пожертвовать - если следовать им слишком сложно или результат того не стоит.</p>
19
<p>Считайте это упражнением. Вам нужно научиться выполнять эти правила при кодинге. А вот в повседневном программировании какими-то из них всегда можно пожертвовать - если следовать им слишком сложно или результат того не стоит.</p>
20
<p>Итак, выберите себе кодерскую задачку, начните её решать и постарайтесь строго следовать этим принципам. Даже если некоторые из них поначалу кажутся глупыми, просто продолжайте.</p>
20
<p>Итак, выберите себе кодерскую задачку, начните её решать и постарайтесь строго следовать этим принципам. Даже если некоторые из них поначалу кажутся глупыми, просто продолжайте.</p>
21
<p>То есть одно значение отступа на весь метод. Так его легче будет читать.</p>
21
<p>То есть одно значение отступа на весь метод. Так его легче будет читать.</p>
22
<p>Вам придётся извлечь и вынести в отдельные методы некоторые фрагменты кода - например, условия и циклы. Этим новым методам нужно будет дать говорящие имена и вызвать их из исходного метода.</p>
22
<p>Вам придётся извлечь и вынести в отдельные методы некоторые фрагменты кода - например, условия и циклы. Этим новым методам нужно будет дать говорящие имена и вызвать их из исходного метода.</p>
23
Видели когда-нибудь подобный код? У него и название есть - "стрелочный код", надо же! Изображение: Джордж Студенко<ul><li>выполняется принцип единственной ответственности (SRP);</li>
23
Видели когда-нибудь подобный код? У него и название есть - "стрелочный код", надо же! Изображение: Джордж Студенко<ul><li>выполняется принцип единственной ответственности (SRP);</li>
24
<li>имена становятся понятнее,</li>
24
<li>имена становятся понятнее,</li>
25
<li>методы - короче, </li>
25
<li>методы - короче, </li>
26
<li>и их удобнее использовать повторно.</li>
26
<li>и их удобнее использовать повторно.</li>
27
</ul><p>Вложенные условия усложняют код, делают его нелинейным.</p>
27
</ul><p>Вложенные условия усложняют код, делают его нелинейным.</p>
28
<p>Мы решили слегка отойти от оригинальной статьи и добавили пояснения к каждому способу. - Пер.</p>
28
<p>Мы решили слегка отойти от оригинальной статьи и добавили пояснения к каждому способу. - Пер.</p>
29
<ul><li><strong>Использовать значения по умолчанию.</strong></li>
29
<ul><li><strong>Использовать значения по умолчанию.</strong></li>
30
</ul><p>Например, ваша программа работает с объектами-фруктами: яблоками и грушами. По конвейеру движутся коробки с фруктами. Некоторые коробки подписаны, некоторые - нет. Если коробка не подписана, то вы считаете, что в ней лежат яблоки, - ну, так договорились.</p>
30
</ul><p>Например, ваша программа работает с объектами-фруктами: яблоками и грушами. По конвейеру движутся коробки с фруктами. Некоторые коробки подписаны, некоторые - нет. Если коробка не подписана, то вы считаете, что в ней лежат яблоки, - ну, так договорились.</p>
31
<p>В этом случае вы можете написать что-то вроде:</p>
31
<p>В этом случае вы можете написать что-то вроде:</p>
32
<p>если коробка подписана, считать, что внутри те фрукты, которые указаны на коробке</p>
32
<p>если коробка подписана, считать, что внутри те фрукты, которые указаны на коробке</p>
33
<p>иначе считать, что там яблоки</p>
33
<p>иначе считать, что там яблоки</p>
34
<p>А ещё вы можете по умолчанию считать, что в коробке яблоки, и менять это значение, только если встретите надпись "груши".</p>
34
<p>А ещё вы можете по умолчанию считать, что в коробке яблоки, и менять это значение, только если встретите надпись "груши".</p>
35
<ul><li><strong>Практиковать ранний возврат из метода.</strong></li>
35
<ul><li><strong>Практиковать ранний возврат из метода.</strong></li>
36
</ul><p>Этот способ подойдёт для тривиального случая. </p>
36
</ul><p>Этот способ подойдёт для тривиального случая. </p>
37
<p>Вместо:</p>
37
<p>Вместо:</p>
38
<p>если условие1 выполняй действие1</p>
38
<p>если условие1 выполняй действие1</p>
39
<p>иначе выполняй действие2</p>
39
<p>иначе выполняй действие2</p>
40
<p>можно написать:</p>
40
<p>можно написать:</p>
41
<p>если условие1 выполняй действие1 и вернись</p>
41
<p>если условие1 выполняй действие1 и вернись</p>
42
<p>а ниже внутри того же метода расположить обработку второго условия - ведь исполнение дойдёт до него, только если не сработает условие1.</p>
42
<p>а ниже внутри того же метода расположить обработку второго условия - ведь исполнение дойдёт до него, только если не сработает условие1.</p>
43
<ul><li><strong>Выносить ветвления кода в отдельные методы.</strong></li>
43
<ul><li><strong>Выносить ветвления кода в отдельные методы.</strong></li>
44
</ul><p>В этом случае методы будут просто вызываться друг за другом, а проверки условий окажутся у них внутри. Если условие не выполнится, программа вернётся на уровень выше и вызовет следующий метод со следующей проверкой.</p>
44
</ul><p>В этом случае методы будут просто вызываться друг за другом, а проверки условий окажутся у них внутри. Если условие не выполнится, программа вернётся на уровень выше и вызовет следующий метод со следующей проверкой.</p>
45
<ul><li><strong>Применить полиморфизм.</strong></li>
45
<ul><li><strong>Применить полиморфизм.</strong></li>
46
</ul><p>Полиморфизм - один из столпов ООП. Благодаря ему один и тот же метод может по-разному реализовываться в иерархии классов.</p>
46
</ul><p>Полиморфизм - один из столпов ООП. Благодаря ему один и тот же метод может по-разному реализовываться в иерархии классов.</p>
47
<p>Здесь автор говорит о том, что конструкцию с несколькими условиями иногда бывает уместно заменить вызовом одного метода.</p>
47
<p>Здесь автор говорит о том, что конструкцию с несколькими условиями иногда бывает уместно заменить вызовом одного метода.</p>
48
<p>Так получится сделать, если создать систему классов-наследников, в которых по-разному определить этот один метод.</p>
48
<p>Так получится сделать, если создать систему классов-наследников, в которых по-разному определить этот один метод.</p>
49
<ul><li><strong>Задействовать шаблон проектирования "Состояние".</strong></li>
49
<ul><li><strong>Задействовать шаблон проектирования "Состояние".</strong></li>
50
</ul><p>Шаблон проектирования "Состояние" полезен, когда в зависимости от состояния системы одно и то же действие (переход к следующему этапу) должно обрабатываться по-разному.</p>
50
</ul><p>Шаблон проектирования "Состояние" полезен, когда в зависимости от состояния системы одно и то же действие (переход к следующему этапу) должно обрабатываться по-разному.</p>
51
<p>Например, кофейный автомат сначала ждёт выбора напитка, потом оплаты, а только после этого готовит кофе.</p>
51
<p>Например, кофейный автомат сначала ждёт выбора напитка, потом оплаты, а только после этого готовит кофе.</p>
52
<p>Метод перехода на следующий шаг можно написать с кучей проверок типа "если сейчас я на шаге N, то делай то-то", а можно создать классы для каждого состояния и прописать действия по переходу на следующий шаг в них.</p>
52
<p>Метод перехода на следующий шаг можно написать с кучей проверок типа "если сейчас я на шаге N, то делай то-то", а можно создать классы для каждого состояния и прописать действия по переходу на следующий шаг в них.</p>
53
<ul><li><strong>Применить шаблон проектирования "Стратегия".</strong></li>
53
<ul><li><strong>Применить шаблон проектирования "Стратегия".</strong></li>
54
</ul><p>Шаблон проектирования "Стратегия" определяет семейство схожих алгоритмов и помещает каждый из них в отдельный класс.</p>
54
</ul><p>Шаблон проектирования "Стратегия" определяет семейство схожих алгоритмов и помещает каждый из них в отдельный класс.</p>
55
<p>В вызывающем классе хранится ссылка на стратегию, которую сейчас нужно использовать. Так что вместо множества проверок типа "если..., то..." вызывается один метод класса-стратегии.</p>
55
<p>В вызывающем классе хранится ссылка на стратегию, которую сейчас нужно использовать. Так что вместо множества проверок типа "если..., то..." вызывается один метод класса-стратегии.</p>
56
<ul><li>код не дублируется,</li>
56
<ul><li>код не дублируется,</li>
57
<li>становится проще</li>
57
<li>становится проще</li>
58
<li>и читается легче.</li>
58
<li>и читается легче.</li>
59
</ul><p>Оборачивая примитивные типы в классы, мы инкапсулируем (скрываем) тип. Если позже в ходе рефакторинга мы захотим изменить примитивные типы, это можно будет сделать в одном месте.</p>
59
</ul><p>Оборачивая примитивные типы в классы, мы инкапсулируем (скрываем) тип. Если позже в ходе рефакторинга мы захотим изменить примитивные типы, это можно будет сделать в одном месте.</p>
60
<p>А ещё такой код легче воспринимать, ведь по сигнатуре объекта-обёртки сразу ясно, что передавать методу в качестве параметров.</p>
60
<p>А ещё такой код легче воспринимать, ведь по сигнатуре объекта-обёртки сразу ясно, что передавать методу в качестве параметров.</p>
61
<p><strong>Примечание переводчика</strong></p>
61
<p><strong>Примечание переводчика</strong></p>
62
<p>В оригинальной книге правило звучит так:<strong>"оборачивайте примитивы и строки"</strong>.</p>
62
<p>В оригинальной книге правило звучит так:<strong>"оборачивайте примитивы и строки"</strong>.</p>
63
<p>Например, если метод принимает параметр типа<strong>int</strong>, это мало о чём говорит. Другое дело, если тип параметра будет, скажем,<strong>Hour</strong>. Мы оборачиваем целое число (часов) в класс. В тот же класс можно добавить проверку допустимых значений, и тогда никто не сможет передать в метод 36 или другое неподходящее число.</p>
63
<p>Например, если метод принимает параметр типа<strong>int</strong>, это мало о чём говорит. Другое дело, если тип параметра будет, скажем,<strong>Hour</strong>. Мы оборачиваем целое число (часов) в класс. В тот же класс можно добавить проверку допустимых значений, и тогда никто не сможет передать в метод 36 или другое неподходящее число.</p>
64
<ul><li>Соблюдается инкапсуляция.</li>
64
<ul><li>Соблюдается инкапсуляция.</li>
65
<li>Появляются подсказки для типов.</li>
65
<li>Появляются подсказки для типов.</li>
66
<li>Можно изолировать схожее поведение в отдельных методах и использовать их повторно.</li>
66
<li>Можно изолировать схожее поведение в отдельных методах и использовать их повторно.</li>
67
</ul><p>Это следствие<a>закона Деметры</a>: "Общайся только с друзьями" (речь об общении между классами с помощью вызова методов друг друга. - Пер.).</p>
67
</ul><p>Это следствие<a>закона Деметры</a>: "Общайся только с друзьями" (речь об общении между классами с помощью вызова методов друг друга. - Пер.).</p>
68
<p>Точку вы используете для вызова методов в Java или С#. Вызовы типа object.getProperty().getSubProperty().doSomething() - это очень плохая практика! Классы не должны знать так много деталей реализации других классов.</p>
68
<p>Точку вы используете для вызова методов в Java или С#. Вызовы типа object.getProperty().getSubProperty().doSomething() - это очень плохая практика! Классы не должны знать так много деталей реализации других классов.</p>
69
<ul><li>Соблюдается инкапсуляция.</li>
69
<ul><li>Соблюдается инкапсуляция.</li>
70
<li>Выполняется принцип открытости/закрытости.</li>
70
<li>Выполняется принцип открытости/закрытости.</li>
71
</ul><p>Вы когда-нибудь встречали странное сокращённое название метода или переменной. Такое, что назначение их без контекста можно понять пятью разными способами?</p>
71
</ul><p>Вы когда-нибудь встречали странное сокращённое название метода или переменной. Такое, что назначение их без контекста можно понять пятью разными способами?</p>
72
<p>Если вам приходится сокращать название метода, возможно, этот метод делает больше, чем должен. То есть нарушен принцип единственной ответственности. Поразмыслите над этим.</p>
72
<p>Если вам приходится сокращать название метода, возможно, этот метод делает больше, чем должен. То есть нарушен принцип единственной ответственности. Поразмыслите над этим.</p>
73
<p>Поразмыслили? А теперь не сокращайте наименования!</p>
73
<p>Поразмыслили? А теперь не сокращайте наименования!</p>
74
<ul><li>Соблюдается SRP.</li>
74
<ul><li>Соблюдается SRP.</li>
75
<li>Вы избегаете путаницы.</li>
75
<li>Вы избегаете путаницы.</li>
76
<li>Нет дублирования кода.</li>
76
<li>Нет дублирования кода.</li>
77
</ul><p>Делайте их небольшими:</p>
77
</ul><p>Делайте их небольшими:</p>
78
<ul><li>15-20 строк на метод,</li>
78
<ul><li>15-20 строк на метод,</li>
79
<li>50 строк на класс,</li>
79
<li>50 строк на класс,</li>
80
<li>10 классов на пакет.</li>
80
<li>10 классов на пакет.</li>
81
</ul><p>В конце концов, если ваш класс специализируется на чём-то одном, он не должен быть большим, верно?</p>
81
</ul><p>В конце концов, если ваш класс специализируется на чём-то одном, он не должен быть большим, верно?</p>
82
<ul><li>Соблюдается SRP.</li>
82
<ul><li>Соблюдается SRP.</li>
83
<li>Модули уменьшаются.</li>
83
<li>Модули уменьшаются.</li>
84
<li>Код становится хорошо<a>согласованным</a>(coherent).</li>
84
<li>Код становится хорошо<a>согласованным</a>(coherent).</li>
85
</ul><p>Один класс должен иметь дело с одним состоянием, максимум с двумя. Так что, если в классе более двух переменных экземпляра, возможно, он нарушает принцип единственной ответственности.</p>
85
</ul><p>Один класс должен иметь дело с одним состоянием, максимум с двумя. Так что, если в классе более двух переменных экземпляра, возможно, он нарушает принцип единственной ответственности.</p>
86
<p><strong>Примечание переводчика</strong></p>
86
<p><strong>Примечание переводчика</strong></p>
87
<p>Переменная экземпляра (instance variable, атрибут) - переменная, которая хранит свойство объекта - экземпляра класса. Этим она отличается от статической переменной (относится к классу, а не к его экземпляру) и от локальной переменной, которая объявляется в членах класса - например, в методах.</p>
87
<p>Переменная экземпляра (instance variable, атрибут) - переменная, которая хранит свойство объекта - экземпляра класса. Этим она отличается от статической переменной (относится к классу, а не к его экземпляру) и от локальной переменной, которая объявляется в членах класса - например, в методах.</p>
88
<p>Кажется, это требование очень сложно выполнить. Уверен, вы видели классы с десятками параметров в конструкторе, так ведь?</p>
88
<p>Кажется, это требование очень сложно выполнить. Уверен, вы видели классы с десятками параметров в конструкторе, так ведь?</p>
89
<p>Отлично! Почему бы не сгруппировать их в один объект? Или ещё раз хорошенько подумать о том, точно ли у этого класса идеальная архитектура и не делает ли он слишком много лишнего.</p>
89
<p>Отлично! Почему бы не сгруппировать их в один объект? Или ещё раз хорошенько подумать о том, точно ли у этого класса идеальная архитектура и не делает ли он слишком много лишнего.</p>
90
<ul><li>В модулях поддерживается сильная связность (high cohesion).</li>
90
<ul><li>В модулях поддерживается сильная связность (high cohesion).</li>
91
<li>Соблюдается инкапсуляция.</li>
91
<li>Соблюдается инкапсуляция.</li>
92
<li>Становится меньше зависимостей между различными сущностями программы.</li>
92
<li>Становится меньше зависимостей между различными сущностями программы.</li>
93
</ul><p>Это похоже на правило<a>№3</a>, только применительно к коллекциям.</p>
93
</ul><p>Это похоже на правило<a>№3</a>, только применительно к коллекциям.</p>
94
<p>Внутри вашего класса может быть по-прежнему простая коллекция. Но, обернув её в класс, вы в будущем сможете легко провести рефакторинг: например, изменить тип коллекции.</p>
94
<p>Внутри вашего класса может быть по-прежнему простая коллекция. Но, обернув её в класс, вы в будущем сможете легко провести рефакторинг: например, изменить тип коллекции.</p>
95
<p><strong>Примечание переводчика</strong></p>
95
<p><strong>Примечание переводчика</strong></p>
96
<p>В книге The ThoughtWorks Anthology автор акцентирует внимание на том, что в таком классе - обёртке над коллекцией вообще не должно быть других членов, кроме этой коллекции. Зато туда можно добавить методы фильтрации или добавления/удаления элементов.</p>
96
<p>В книге The ThoughtWorks Anthology автор акцентирует внимание на том, что в таком классе - обёртке над коллекцией вообще не должно быть других членов, кроме этой коллекции. Зато туда можно добавить методы фильтрации или добавления/удаления элементов.</p>
97
<ul><li>Появляются более функциональные классы-обёртки над тривиальными коллекциями.</li>
97
<ul><li>Появляются более функциональные классы-обёртки над тривиальными коллекциями.</li>
98
<li>Остаётся только одно место, которое отвечает за поведение коллекции, - удобно, если нужно что-то исправить.</li>
98
<li>Остаётся только одно место, которое отвечает за поведение коллекции, - удобно, если нужно что-то исправить.</li>
99
<li>Соблюдается инкапсуляция.</li>
99
<li>Соблюдается инкапсуляция.</li>
100
</ul><p>Не принимайте решений вне класса, позвольте ему самому заниматься своим делом. Иными словами, следуйте принципу "Рассказывай, а не спрашивай".</p>
100
</ul><p>Не принимайте решений вне класса, позвольте ему самому заниматься своим делом. Иными словами, следуйте принципу "Рассказывай, а не спрашивай".</p>
101
<ul><li>Соблюдается принцип открытости/закрытости.</li>
101
<ul><li>Соблюдается принцип открытости/закрытости.</li>
102
</ul><p>Теперь вы знаете всё! Следовать этим принципам сперва будет сложно. Появится искушение всё бросить. Но воспринимайте это как вызов себе - соберитесь и упражняйтесь (никаких оправданий!).</p>
102
</ul><p>Теперь вы знаете всё! Следовать этим принципам сперва будет сложно. Появится искушение всё бросить. Но воспринимайте это как вызов себе - соберитесь и упражняйтесь (никаких оправданий!).</p>
103
<p>А вот строго выполнять все эти правила в ежедневной разработке вовсе не обязательно. Помните про компромиссные варианты и выбирайте то, что лучше.</p>
103
<p>А вот строго выполнять все эти правила в ежедневной разработке вовсе не обязательно. Помните про компромиссные варианты и выбирайте то, что лучше.</p>
104
<a><b>Бесплатный курс по Python ➞</b>Мини-курс для новичков и для опытных кодеров. 4 крутых проекта в портфолио, живое общение со спикером. Кликните и узнайте, чему можно научиться на курсе. Смотреть программу</a>
104
<a><b>Бесплатный курс по Python ➞</b>Мини-курс для новичков и для опытных кодеров. 4 крутых проекта в портфолио, живое общение со спикером. Кликните и узнайте, чему можно научиться на курсе. Смотреть программу</a>