0 added
0 removed
Original
2026-01-01
Modified
2026-03-10
1
<p>Совсем недавно мы говорили о том,<a>что такое чистый код</a>в контексте Android-разработки. Теперь расскажем, как писать код в соответствии с SOLID - принципами проектирования хорошего кода.</p>
1
<p>Совсем недавно мы говорили о том,<a>что такое чистый код</a>в контексте Android-разработки. Теперь расскажем, как писать код в соответствии с SOLID - принципами проектирования хорошего кода.</p>
2
<h2>1. SRP - Принцип единой ответственности</h2>
2
<h2>1. SRP - Принцип единой ответственности</h2>
3
<p>В данном случае каждый класс должен отвечать за что-нибудь одно. Для изменения класса не должно быть больше одной причины. Да, вы можете добавлять в класс всё, что пожелаете, но делать этого не нужно. А вот разделять большие классы на более мелкие и избегать<a>God Class</a>- хорошая практика.</p>
3
<p>В данном случае каждый класс должен отвечать за что-нибудь одно. Для изменения класса не должно быть больше одной причины. Да, вы можете добавлять в класс всё, что пожелаете, но делать этого не нужно. А вот разделять большие классы на более мелкие и избегать<a>God Class</a>- хорошая практика.</p>
4
<p>Для примера представим, что у нас есть RecyclerView.Adapter с логикой внутри onBindViewHolder:</p>
4
<p>Для примера представим, что у нас есть RecyclerView.Adapter с логикой внутри onBindViewHolder:</p>
5
<p>Так как RecyclerView.Adapter имеет логику внутри onBindViewHolder, налицо противоречие принципу единой ответственности.</p>
5
<p>Так как RecyclerView.Adapter имеет логику внутри onBindViewHolder, налицо противоречие принципу единой ответственности.</p>
6
<h2>2. OCP - Принцип открытости-закрытости</h2>
6
<h2>2. OCP - Принцип открытости-закрытости</h2>
7
<p>Принято, чтобы программные объекты были открыты для расширения, однако закрыты для модификации. Это значит, что когда вы пишете класс A, а потом ваши коллеги по команде желают внести изменения в его функцию, они могут это сделать без проблем, расширив класс A и не внося при этом изменений.</p>
7
<p>Принято, чтобы программные объекты были открыты для расширения, однако закрыты для модификации. Это значит, что когда вы пишете класс A, а потом ваши коллеги по команде желают внести изменения в его функцию, они могут это сделать без проблем, расширив класс A и не внося при этом изменений.</p>
8
<p>В качестве примера рассмотрим класс RecyclerView.Adapter - его можно легко расширить и создать свой адаптер с настраиваемым поведением, не меняя существующий класс RecyclerView.Adapter.</p>
8
<p>В качестве примера рассмотрим класс RecyclerView.Adapter - его можно легко расширить и создать свой адаптер с настраиваемым поведением, не меняя существующий класс RecyclerView.Adapter.</p>
9
<h2>3. LSP - принцип подстановки Лисков</h2>
9
<h2>3. LSP - принцип подстановки Лисков</h2>
10
<p>Согласно этому принципу, дочерние классы не должны нарушать определения типов родительского класса. Никогда.</p>
10
<p>Согласно этому принципу, дочерние классы не должны нарушать определения типов родительского класса. Никогда.</p>
11
<p>Это значит, что подкласс должен переопределять методы родительского класса, не нарушающие функциональность родительского класса. Допустим, вы создаете интерфейсный класс с прослушивателем onClick(), а потом используете прослушиватель в MyActivity и назначаете ему всплывающее действие в момент вызова onClick().</p>
11
<p>Это значит, что подкласс должен переопределять методы родительского класса, не нарушающие функциональность родительского класса. Допустим, вы создаете интерфейсный класс с прослушивателем onClick(), а потом используете прослушиватель в MyActivity и назначаете ему всплывающее действие в момент вызова onClick().</p>
12
<h2>4. ISP - Принцип разделения интерфейса</h2>
12
<h2>4. ISP - Принцип разделения интерфейса</h2>
13
<p>Согласно этому принципу, ни один клиент не должен зависеть от неиспользуемых им методов. Допустим, вы хотите создать класс A с последующей его реализацией в другом классе B. Не надо для этого переопределять внутри класса B все методы класса A.</p>
13
<p>Согласно этому принципу, ни один клиент не должен зависеть от неиспользуемых им методов. Допустим, вы хотите создать класс A с последующей его реализацией в другом классе B. Не надо для этого переопределять внутри класса B все методы класса A.</p>
14
<p>Как обычно, не обойтись без примера. Представьте, что внутри следующего кода нам надо выполнить SearchView.OnQueryTextListener() и только метод onQuerySubmit().</p>
14
<p>Как обычно, не обойтись без примера. Представьте, что внутри следующего кода нам надо выполнить SearchView.OnQueryTextListener() и только метод onQuerySubmit().</p>
15
<p>Чтобы этого добиться, можно просто создать обратный вызов и класс, распространяемый на SearchView.OnQueryTextListener().</p>
15
<p>Чтобы этого добиться, можно просто создать обратный вызов и класс, распространяемый на SearchView.OnQueryTextListener().</p>
16
<p>А вот реализация:</p>
16
<p>А вот реализация:</p>
17
<p>Если у нас Kotlin, можем применить функцию-расширение:</p>
17
<p>Если у нас Kotlin, можем применить функцию-расширение:</p>
18
<p>А теперь магия:</p>
18
<p>А теперь магия:</p>
19
<h2>5. DIP - Принцип инверсии зависимостей</h2>
19
<h2>5. DIP - Принцип инверсии зависимостей</h2>
20
<p>Он зависит от абстракций и не зависит от конкрементов. Также этот принцип определяется следующими пунктами: 1. Высокоуровневые модули не должны зависеть от низкоуровневых. Оба модуля должны зависеть от абстракций. 2. Абстракции не должны зависеть от деталей, а детали должны зависеть от абстракций.</p>
20
<p>Он зависит от абстракций и не зависит от конкрементов. Также этот принцип определяется следующими пунктами: 1. Высокоуровневые модули не должны зависеть от низкоуровневых. Оба модуля должны зависеть от абстракций. 2. Абстракции не должны зависеть от деталей, а детали должны зависеть от абстракций.</p>
21
<p>Высокоуровневые модули со сложной логикой должны легко применяться повторно, не подвергаясь изменениям в низкоуровневых модулях, которые предоставляют служебные функции. Дабы этого достичь, надо ввести абстракцию, отделяющую друг от друга модули высокого и низкого уровня.</p>
21
<p>Высокоуровневые модули со сложной логикой должны легко применяться повторно, не подвергаясь изменениям в низкоуровневых модулях, которые предоставляют служебные функции. Дабы этого достичь, надо ввести абстракцию, отделяющую друг от друга модули высокого и низкого уровня.</p>
22
<p>В качестве простого примера можно привести<strong>шаблон MVP</strong>. Если у вас есть объект интерфейсов, помогающий взаимодействовать с конкретными классами, то это значит, что классам пользовательского интерфейса (Activity / Fragment) не надо знать фактическую реализацию методов в Presenter. Но если существуют какие-нибудь изменения внутри, то классы пользовательского интерфейса не должны знать об этих изменениях.</p>
22
<p>В качестве простого примера можно привести<strong>шаблон MVP</strong>. Если у вас есть объект интерфейсов, помогающий взаимодействовать с конкретными классами, то это значит, что классам пользовательского интерфейса (Activity / Fragment) не надо знать фактическую реализацию методов в Presenter. Но если существуют какие-нибудь изменения внутри, то классы пользовательского интерфейса не должны знать об этих изменениях.</p>
23
<p>Лучше всего рассматривать этот принцип на примере:</p>
23
<p>Лучше всего рассматривать этот принцип на примере:</p>
24
<p>Таким образом, мы создаём интерфейс, абстрагирующий реализацию presenter, при этом наш класс представления сохраняет ссылку на PresenterInterface.</p>
24
<p>Таким образом, мы создаём интерфейс, абстрагирующий реализацию presenter, при этом наш класс представления сохраняет ссылку на PresenterInterface.</p>
25
<p><em>По материалам статьи "<a>Understanding Clean Code in Android</a>".</em></p>
25
<p><em>По материалам статьи "<a>Understanding Clean Code in Android</a>".</em></p>
26
26