SOLID: принципы, 5 правил ООП и применение в разработке

Автор: IT Sectr Опубликовано: 2026-05-11 Время чтения: 10 мин

SOLID — пять принципов объектно-ориентированного программирования, сформулированные Robert C. Martin (Uncle Bob) в начале 2000-х годов. По данным DigitalOcean, 2024, SOLID расшифровывается как Single Responsibility, Open-Closed, Liskov Substitution, Interface Segregation и Dependency Inversion. Эти принципы составляют основу Clean Architecture и применяются в Android-разработке (MVP, MVVM, Clean Architecture) и iOS (VIPER, TCA).

Главное

  • SOLID — акроним пяти принципов ООП: SRP, OCP, LSP, ISP, DIP, сформулированных Robert C. Martin для создания гибкого и поддерживаемого кода.
  • SRP (Single Responsibility) — каждый класс имеет одну причину для изменения, одна ответственность на модуль.
  • OCP (Open-Closed) — классы открыты для расширения, но закрыты для модификации, реализуется через наследование и полиморфизм.
  • LSP (Liskov Substitution) — объекты подклассов должны заменять объекты базового класса без изменения корректности программы.
  • ISP (Interface Segregation) — клиенты не должны зависеть от интерфейсов, которые они не используют, интерфейсы должны быть узкими и специфичными.
  • DIP (Dependency Inversion) — модули верхнего уровня не зависят от модулей нижнего уровня, оба зависят от абстракций.

Что такое SOLID? Обзор пяти принципов

SOLID — мнемонический акроним, обозначающий пять принципов объектно-ориентированного проектирования. Термин введён Robert C. Martin в статье «Design Principles and Design Patterns» (2000), а позже популяризирован в книге «Agile Software Development: Principles, Patterns, and Practices» (2002). SOLID не является фреймворком или библиотекой — это набор практик, которые делают код менее связанным, более тестируемым и удобным для изменений.

По данным Clean Coder Blog, 2014, каждый принцип SOLID решает определённую проблему проектирования: SRP борется с God-классами, OCP — с каскадными изменениями, LSP — с некорректным наследованием, ISP — с жирными интерфейсами, DIP — с жёсткой связанностью. Вместе они формируют фундамент Clean Architecture, который используется в Android-проектах с MVP, MVVM и MVI.

SRP: Single Responsibility Principle

Single Responsibility Principle (SRP) — принцип единственной ответственности. Формулировка: «У класса должна быть только одна причина для изменения». Это означает, что каждый модуль или класс отвечает ровно за одну функциональность или одну сущность предметной области. Если класс управляет и пользователями, и отправкой email — у него две причины для изменения, что нарушает SRP.

По данным Robert C. Martin, 2002, SRP — самый важный и одновременно самый нарушаемый принцип. В мобильной разработке SRP часто нарушают в Activity/Fragment, совмещая UI-логику, навигацию, работу с сетью и бизнес-логику. Решение — вынести каждый слой в отдельный класс: ViewModel для UI-логики, Repository для данных, NavController для навигации.

Пример SRP: деление UserManager

Рассмотрим класс UserManager, который и загружает профиль, и сохраняет настройки, и отправляет email. Это три разные ответственности, каждую из которых нужно выделить в отдельный класс: UserProfileRepository (загрузка), UserSettingsStorage (сохранение) и EmailService (отправка). Клиентский код (ViewModel) использует все три через Dependency Injection, а каждый класс легко тестируется изолированно и меняется без влияния на другие.

kotlin
// ❌ Нарушение SRP: Activity знает про сеть, БД и UI
class ProfileActivity : AppCompatActivity() {
    fun loadProfile() {
        api.getUser() // Сетевой вызов
        db.saveUser()    // Работа с БД
        updateUI()         // Обновление UI
    }
}

// ✅ SRP соблюдён: слои разделены
class ProfileViewModel : ViewModel() {
    private val repo = UserRepository()
    fun loadProfile() { repo.getUser() }
}

Признаки нарушения SRP: класс содержит более 200 строк, имеет методы из разных предметных областей, часто изменяется по разным причинам. Для Android-разработки правило простое: Activity отвечает только за жизненный цикл экрана, ViewModel — за состояние UI, Repository — за источники данных.

SRP и микро-сервисная архитектура

Принцип SRP применим не только к классам, но и к архитектуре уровня сервиса. Каждый микросервис отвечает за одну доменную сущность: UserService — только пользователи, PaymentService — только платежи, NotificationService — только уведомления. Это позволяет масштабировать, деплоить и тестировать сервисы независимо. В мобильном приложении SRP на уровне микросервисов проявляется в разделении API-клиентов по доменам.

OCP: Open-Closed Principle

Open-Closed Principle (OCP) — принцип открытости / закрытости. Классы должны быть открыты для расширения (можно добавить новое поведение) и закрыты для модификации (существующий код не меняется). Достигается через полиморфизм, абстрактные классы и интерфейсы. Вместо того чтобы добавлять if-else в существующий метод, создаётся новая реализация интерфейса.

По данным Clean Coder Blog, 2014, OCP наиболее эффективен в связке с паттерном Strategy. Например, если приложение поддерживает разные способы оплаты (Google Pay, Apple Pay, PayPal), не нужно добавлять switch-case в платёжный процессор. Каждый способ оплаты реализует общий интерфейс PaymentGateway, и новая платёжная система добавляется как новый класс без изменения существующих.

kotlin
// ✅ OCP: открыт для расширения, закрыт для модификации
interface PaymentGateway {
    fun processPayment(amount: Double): Boolean
}

class GooglePayGateway : PaymentGateway {
    override fun processPayment(amount: Double) = true
}

// Новая платёжная система — без изменения существующего кода
class ApplePayGateway : PaymentGateway {
    override fun processPayment(amount: Double) = true
}

LSP: Liskov Substitution Principle

Liskov Substitution Principle (LSP) — принцип подстановки Барбары Лисков. Если S — подтип T, то объекты T можно заменить объектами S без изменения свойств программы. Формально: функция, использующая базовый класс, должна корректно работать с любым его подклассом. Если подкласс выбрасывает исключение там, где базовый класс не выбрасывает — LSP нарушен.

По данным Robert C. Martin, 2002, LSP — самый сложный для понимания принцип SOLID. Классический пример нарушения — класс Square (квадрат), наследующий от Rectangle (прямоугольник). Если setWidth для Square устанавливает и ширину, и высоту, то клиентский код, ожидающий поведение Rectangle, получит неожиданный результат. В мобильной разработке LSP часто нарушается при наследовании ViewModel, когда дочерняя ViewModel добавляет обязательные зависимости.

kotlin
// ❌ Нарушение LSP: Square ломает поведение Rectangle
open class Rectangle(open var width: Int, open var height: Int)

class Square(side: Int) : Rectangle(side, side) {
    override var width
        get() = super.width
        set(value) { super.setBoth(value, value) }
}

ISP: Interface Segregation Principle

Interface Segregation Principle (ISP) — принцип разделения интерфейсов. Клиенты не должны зависеть от интерфейсов, которые они не используют. Вместо одного «жирного» интерфейса создаётся несколько узких специализированных. Если класс реализует интерфейс, но часть методов выбрасывает исключение UnsupportedOperationException или оставляет пустыми — это явный признак нарушения ISP.

По данным DigitalOcean, 2024, ISP особенно актуален в мобильной разработке при проектировании ViewModel и Repository. Вместо одного интерфейса UserRepository со всеми CRUD-методами лучше создать QueryUserRepository (только чтение) и CommandUserRepository (запись). Тогда читающий клиент (элемент UI) зависит только от Query-интерфейса и не знает про методы записи.

kotlin
// ❌ Жирный интерфейс — клиент вынужден реализовывать ненужные методы
interface UserOperations {
    fun getUser(id: String): User
    fun saveUser(user: User)
    fun deleteUser(id: String)
    fun exportUsers(): File
}

// ✅ ISP: разделённые интерфейсы
interface UserReader { fun getUser(id: String): User }
interface UserWriter { fun saveUser(user: User) }
interface UserDeleter { fun deleteUser(id: String) }

DIP: Dependency Inversion Principle

Dependency Inversion Principle (DIP) — принцип инверсии зависимостей. Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба уровня должны зависеть от абстракций (интерфейсов). Абстракции не должны зависеть от деталей — детали зависят от абстракций. Это не «Dependency Injection» (внедрение зависимостей), хотя DI — частый способ реализации DIP.

По данным Robert C. Martin, 2019, DIP — основа Clean Architecture. ViewModel (верхний уровень) не должен напрямую создавать экземпляр RetrofitApi (деталь). Вместо этого ViewModel зависит от интерфейса UserRepository, а конкретная реализация UserRepositoryImpl с Retrofit передаётся через конструктор. В Android DIP реализуется через Hilt/Dagger или Koin: все зависимости предоставляются через DI-контейнер.

kotlin
// ✅ DIP: Module зависит от абстракции, а не от детали
class UserRepositoryImpl(
    private val api: UserApi,   // Зависим от интерфейса
    private val db: UserDao     // Зависим от интерфейса
) : UserRepository {

    override suspend fun getUser(id: String): User {
        return api.fetchUser(id)
    }
}

// Hilt DI: детали связываются через модуль DI
@Module
object NetworkModule {
    @Provides
    fun provideUserApi(retrofit: Retrofit): UserApi =
        retrofit.create(UserApi::class.java)
}

Применение SOLID в мобильной разработке

SOLID в мобильной разработке применяется на всех уровнях: от архитектуры приложения до отдельных классов. В Android-проектах Clean Architecture делит код на три слоя: domain (бизнес-логика — независит от фреймворков), data (репозитории, API, БД) и presentation (UI, ViewModel). Domain-слой использует принципы SOLID: use case (SRP), интерфейсы репозиториев (DIP), entity-классы (OCP + LSP).

По данным Android Developers Guide, 2025, SRP в Android проявляется в разделении ViewModel, Repository и Mapper. OCP — при добавлении новых источников данных через интерфейс DataSource. LSP — в единообразной обработке Result от разных репозиториев. ISP — в CQRS-подходе (разделение Read/Write репозиториев). DIP — через Hilt/Koin для внедрения зависимостей.

ПринципПроблема без негоРешение в мобильном проекте
SRPActivity на 1000+ строкViewModel + UseCase + Repository
OCPswitch-case по типу платежаStrategy: PaymentGateway интерфейс
LSPБаг при замене BaseViewModelПроверка контракта подклассов
ISPUnsupportedOperationExceptionReader / Writer разделение
DIPViewModel создаёт Retrofit вручнуюHilt / Koin DI контейнер

Типичные ошибки при применении SOLID

Ошибки SOLID чаще всего связаны с избыточным усложнением кода. Первая — следование принципам буквально без учёта контекста. Разделение одного класса UserService на 10 интерфейсов и 15 классов ради «чистого» ISP — это оверинжиниринг. SOLID — инструмент, а не цель. Вторая ошибка — путаница между SRP и «один метод = одна ответственность». Класс может иметь несколько методов, если все они относятся к одной зоне ответственности.

По данным Simple Thread, 2024, третья ошибка — игнорирование LSP при наследовании ViewModel в Android. Если базовая ViewModel ожидает LiveData, а дочерняя использует StateFlow — клиентский код, подписанный на LiveData, не получит обновлений. Четвёртая — нарушение DIP ради тестирования: RepositoryImpl напрямую создаёт экземпляр OkHttpClient, что делает модульное тестирование невозможным.

Правило золотой середины: применяй SOLID, когда это решает реальную проблему (частые изменения, сложность тестирования, дублирование). Для простых CRUD-экранов строгое следование всем пяти принципам избыточно. Для бизнес-логики, финансовых расчётов и API-взаимодействий SOLID обязателен.

SOLID и Clean Architecture связь

Clean Architecture (Robert C. Martin, 2012) — прямое применение SOLID на уровне слоёв приложения. SRP определяет границы use case (каждый use case — один класс). OCP реализуется через интерфейсы репозиториев (Data Layer может меняться без изменения Domain). ISP даёт разделение Use Case на input/output boundary. DIP — направление зависимостей внутрь Domain-слоя. LSP гарантирует, что любая реализация репозитория заменяема без поломки use case.

Часто задаваемые вопросы

Что такое SOLID простыми словами?

SOLID — пять правил написания кода, чтобы его было легко менять, тестировать и понимать. Каждая буква — один принцип: не пиши большие классы (SRP), не меняй существующий код — добавляй новый (OCP), не ломай поведение наследников (LSP) и другие.

Какой принцип SOLID самый важный?

SRP (Single Responsibility) считается самым важным, потому что его нарушение ведёт к God-классам — огромным классам, которые сложно тестировать и изменять. Однако без DIP (Dependency Inversion) код остаётся жёстко связанным, что тоже критично.

SOLID обязателен для мобильной разработки?

Не обязателен, но крайне желателен для коммерческих проектов с длинным жизненным циклом. Для простых приложений (один экран, нет бизнес-логики) SOLID может быть избыточным. Для проектов на 50+ экранов и 3+ разработчиков SOLID — необходимый минимум.

Что будет, если не соблюдать SOLID?

Последствия: классы становятся «жирными» (1000+ строк), изменение в одном месте ломает три других, невозможно написать модульные тесты, добавление новой фичи занимает недели вместо дней. Со временем код превращается в «Big Ball of Mud» — запутанный и хрупкий.

Как проверить, соблюдается ли SOLID в проекте?

Признаки соблюдения: каждый класс меньше 200 строк, изменение фичи не затрагивает 5+ файлов, тесты пишутся без мокирования 10 зависимостей, новый разработчик понимает структуру за день. Инструменты вроде SonarQube и detekt помогают выявить нарушения SRP и DIP.

Итоги

  • SOLID — пять принципов ООП (SRP, OCP, LSP, ISP, DIP) для создания гибкого и поддерживаемого кода
  • SRP — каждая сущность отвечает за одну задачу, решает проблему God-классов
  • OCP — расширение через полиморфизм, а не модификацию существующего кода
  • LSP — наследники не должны ломать поведение базового класса
  • ISP — узкие интерфейсы вместо универсальных «швейцарских ножей»
  • DIP — зависимость от абстракций, внедрение через Hilt/Koin в Android
  • SOLID обязателен для Clean Architecture и коммерческих мобильных проектов

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также