HTML Diff
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>