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) — принцип на единната отговорност. Формулировка: „Един клас трябва да има само една причина за промяна.“ Това означава, че всеки модул или клас отговаря точно за една функционалност или една домейн единица. Ако един клас управлява и потребителите, и изпращането на имейли — той има две причини за промяна, което нарушава SRP.

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

Пример SRP: разделяне на UserManager

Разгледайте класа UserManager, който зарежда профила, запазва настройки и изпраща имейли. Това са три различни отговорности, всяка трябва да бъде отделена в отделен клас: 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Проверка на контракта на подкласовете
ISPUnsupportedOperationExceptionРазделяне на Reader / Writer
DIPViewModel ръчно създава RetrofitHilt / Koin DI контейнер

Типични грешки при прилагане на SOLID

Грешки SOLID най-често са свързани с прекомерно усложняване на кода. Първа — буквално спазване на принципите без отчитане на контекста. Разделянето на един клас UserService на 10 интерфейса и 15 класа за „чист“ ISP е overengineering. 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 може да се променя без промяна на Domain). ISP предоставя разделяне на Use Case на входна/изходна граница. 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+ файла, тестовете се пишат без mock-ване на 10 зависимости, нов програмист разбира структурата за един ден. Инструменти като SonarQube и detekt помагат за откриване на нарушения на SRP и DIP.

Обобщение

  • SOLID — пет OOP принципа (SRP, OCP, LSP, ISP, DIP) за гъвкав и поддържаем код
  • SRP — всяка единица отговаря за една задача, решава проблема с God-класовете
  • OCP — разширение чрез полиморфизъм, а не модификация на съществуващ код
  • LSP — наследниците не трябва да нарушават поведението на базовия клас
  • ISP — тесни интерфейси вместо универсално „швейцарско ножче“
  • DIP — зависимост от абстракции, вмъкване чрез Hilt/Koin в Android
  • SOLID е задължителен за Clean Architecture и търговски мобилни проекти

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

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също