SOLID: принципи, 5 правила OOP и примена у развоју

Аутор: 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 — акроним пет принципа OOP: 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Провера уговора подкласа
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+ датотека, тестови се пишу без мокирања 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. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође