SOLID: zasady, 5 reguł OOP i zastosowanie w programowaniu

Autor: IT Sectr Opublikowano: 2026-05-11 Czas czytania: 10 min

SOLID — pięć zasad programowania obiektowego, sformułowane przez Robert C. Martin (Uncle Bob) na początku lat 2000. Według DigitalOcean, 2024, SOLID to akronim od Single Responsibility, Open-Closed, Liskov Substitution, Interface Segregation i Dependency Inversion. Zasady te stanowią podstawę Clean Architecture i są stosowane w Android (MVP, MVVM, Clean Architecture) oraz iOS (VIPER, TCA).

Najważniejsze

  • SOLID — akronim pięciu zasad OOP: SRP, OCP, LSP, ISP, DIP, sformułowanych przez Robert C. Martin do tworzenia elastycznego i łatwego w utrzymaniu kodu.
  • SRP (Single Responsibility) — każda klasa ma jeden powód do zmiany, jedna odpowiedzialność na moduł.
  • OCP (Open-Closed) — klasy otwarte na rozszerzanie, ale zamknięte na modyfikację, realizowane przez dziedziczenie i polimorfizm.
  • LSP (Liskov Substitution) — obiekty podklas powinny zastępować obiekty klasy bazowej bez zmiany poprawności programu.
  • ISP (Interface Segregation) — klienci nie powinni zależeć od interfejsów, których nie używają, interfejsy powinny być wąskie i specyficzne.
  • DIP (Dependency Inversion) — moduły wyższego poziomu nie zależą od modułów niższego poziomu, oba zależą od abstrakcji.

Co to jest SOLID? Przegląd pięciu zasad

SOLID — mnemoniczny akronim oznaczający pięć zasad projektowania obiektowego. Termin został wprowadzony przez Robert C. Martin w artykule „Design Principles and Design Patterns” (2000), a później spopularyzowany w książce „Agile Software Development: Principles, Patterns, and Practices” (2002). SOLID nie jest frameworkiem ani biblioteką — to zbiór praktyk, które sprawiają, że kod jest mniej powiązany, bardziej testowalny i łatwiejszy do modyfikacji.

Według Clean Coder Blog, 2014, każda zasada SOLID rozwiązuje konkretny problem projektowy: SRP zwalcza God-klasy, OCP — zmiany kaskadowe, LSP — nieprawidłowe dziedziczenie, ISP — grube interfejsy, DIP — ścisłe powiązania. Razem tworzą fundament Clean Architecture, który jest używany w projektach Android z MVP, MVVM i MVI.

SRP: Single Responsibility Principle

Single Responsibility Principle (SRP) — zasada jednej odpowiedzialności. Brzmienie: „Klasa powinna mieć tylko jeden powód do zmiany”. Oznacza to, że każdy moduł lub klasa odpowiada dokładnie za jedną funkcjonalność lub jedną encję dziedziny. Jeśli klasa zarządza zarówno użytkownikami, jak i wysyłką email — ma dwa powody do zmiany, co narusza SRP.

Według Robert C. Martin, 2002, SRP to najważniejsza i jednocześnie najczęściej naruszana zasada. W programowaniu mobilnym SRP jest często naruszane w Activity/Fragment, łącząc logikę UI, nawigację, pracę z siecią i logikę biznesową. Rozwiązanie — wydzielenie każdej warstwy do osobnej klasy: ViewModel dla logiki UI, Repository dla danych, NavController dla nawigacji.

Przykład SRP: podział UserManager

Rozważmy klasę UserManager, która ładuje profil, zapisuje ustawienia i wysyła emaile. To trzy różne odpowiedzialności, z których każdą należy wydzielić do osobnej klasy: UserProfileRepository (ładowanie), UserSettingsStorage (zapisywanie) i EmailService (wysyłka). Kod kliencki (ViewModel) korzysta ze wszystkich trzech przez Dependency Injection, a każda klasa jest łatwo testowana izolowanie i zmienia się bez wpływu na pozostałe.

kotlin
// ❌ Naruszenie SRP: Activity wie o sieci, BD i UI
class ProfileActivity : AppCompatActivity() {
    fun loadProfile() {
        api.getUser() // Wywołanie sieciowe
        db.saveUser()    // Praca z BD
        updateUI()         // Aktualizacja UI
    }
}

// ✅ SRP przestrzegane: warstwy rozdzielone
class ProfileViewModel : ViewModel() {
    private val repo = UserRepository()
    fun loadProfile() { repo.getUser() }
}

Objawy naruszenia SRP: klasa zawiera ponad 200 linii, ma metody z różnych dziedzin, często zmienia się z różnych powodów. Dla Android reguła jest prosta: Activity odpowiada tylko za cykl życia ekranu, ViewModel — za stan UI, Repository — za źródła danych.

SRP a architektura mikroserwisowa

Zasada SRP ma zastosowanie nie tylko do klas, ale także do architektury na poziomie usług. Każdy mikroserwis odpowiada za jedną encję domenową: UserService — tylko użytkownicy, PaymentService — tylko płatności, NotificationService — tylko powiadomienia. Pozwala to na skalowanie, wdrażanie i testowanie serwisów niezależnie. W aplikacji mobilnej SRP na poziomie mikroserwisów przejawia się w podziale klientów API według domen.

OCP: Open-Closed Principle

Open-Closed Principle (OCP) — zasada otwartości/zamknięcia. Klasy powinny być otwarte na rozszerzanie (można dodać nowe zachowanie) i zamknięte na modyfikację (istniejący kod się nie zmienia). Osiąga się to przez polimorfizm, klasy abstrakcyjne i interfejsy. Zamiast dodawać if-else w istniejącej metodzie, tworzy się nową implementację interfejsu.

Według Clean Coder Blog, 2014, OCP jest najbardziej skuteczny w połączeniu z wzorcem Strategy. Na przykład, jeśli aplikacja obsługuje różne metody płatności (Google Pay, Apple Pay, PayPal), nie trzeba dodawać switch-case w procesorze płatności. Każda metoda płatności implementuje wspólny interfejs PaymentGateway, a nowy system płatności dodaje się jako nowa klasa bez zmiany istniejącego kodu.

kotlin
// ✅ OCP: otwarty na rozszerzanie, zamknięty na modyfikację
interface PaymentGateway {
    fun processPayment(amount: Double): Boolean
}

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

// Nowy system płatności — bez zmiany istniejącego kodu
class ApplePayGateway : PaymentGateway {
    override fun processPayment(amount: Double) = true
}

LSP: Liskov Substitution Principle

Liskov Substitution Principle (LSP) — zasada podstawienia Barbary Liskov. Jeśli S jest podtypem T, to obiekty T można zastąpić obiektami S bez zmiany właściwości programu. Formalnie: funkcja używająca klasy bazowej powinna działać poprawnie z dowolną jej podklasą. Jeśli podklasa wyrzuca wyjątek tam, gdzie klasa bazowa go nie wyrzuca — LSP jest naruszone.

Według Robert C. Martin, 2002, LSP to najtrudniejsza do zrozumienia zasada SOLID. Klasyczny przykład naruszenia — klasa Square (kwadrat) dziedzicząca po Rectangle (prostokąt). Jeśli setWidth dla Square ustawia zarówno szerokość, jak i wysokość, to kod kliencki oczekujący zachowania Rectangle otrzyma nieoczekiwany wynik. W programowaniu mobilnym LSP jest często naruszane przy dziedziczeniu ViewModel, gdy podrzędna ViewModel dodaje obowiązkowe zależności.

kotlin
// ❌ Naruszenie LSP: Square psuje zachowanie 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) — zasada segregacji interfejsów. Klienci nie powinni polegać na interfejsach, których nie używają. Zamiast jednego „grubego” interfejsu tworzy się kilka wąskich, wyspecjalizowanych. Jeśli klasa implementuje interfejs, ale część metod wyrzuca wyjątek UnsupportedOperationException lub pozostawia puste — to wyraźny znak naruszenia ISP.

Według DigitalOcean, 2024, ISP jest szczególnie istotny w programowaniu mobilnym przy projektowaniu ViewModel i Repository. Zamiast jednego interfejsu UserRepository ze wszystkimi metodami CRUD lepiej stworzyć QueryUserRepository (tylko odczyt) i CommandUserRepository (zapis). Wtedy klient czytający (element UI) zależy tylko od interfejsu Query i nie wie o metodach zapisu.

kotlin
// ❌ Gruby interfejs — klient zmuszony implementować niepotrzebne metody
interface UserOperations {
    fun getUser(id: String): User
    fun saveUser(user: User)
    fun deleteUser(id: String)
    fun exportUsers(): File
}

// ✅ ISP: rozdzielone interfejsy
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) — zasada odwrócenia zależności. Moduły wyższego poziomu nie powinny zależeć od modułów niższego poziomu. Oba poziomy powinny zależeć od abstrakcji (interfejsów). Abstrakcje nie powinny zależeć od szczegółów — szczegóły zależą od abstrakcji. To nie to samo co „Dependency Injection” (wstrzykiwanie zależności), choć DI jest częstym sposobem implementacji DIP.

Według Robert C. Martin, 2019, DIP to podstawa Clean Architecture. ViewModel (wyższy poziom) nie powinien bezpośrednio tworzyć instancji RetrofitApi (szczegół). Zamiast tego ViewModel zależy od interfejsu UserRepository, a konkretna implementacja UserRepositoryImpl z Retrofit jest przekazywana przez konstruktor. W Android DIP jest implementowane przez Hilt/Dagger lub Koin: wszystkie zależności są dostarczane przez kontener DI.

kotlin
// ✅ DIP: Moduł zależy od abstrakcji, a nie od szczegółu
class UserRepositoryImpl(
    private val api: UserApi,   // Zależy od interfejsu
    private val db: UserDao     // Zależy od interfejsu
) : UserRepository {

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

// Hilt DI: szczegóły są wiązane przez moduł DI
@Module
object NetworkModule {
    @Provides
    fun provideUserApi(retrofit: Retrofit): UserApi =
        retrofit.create(UserApi::class.java)
}

Zastosowanie SOLID w programowaniu mobilnym

SOLID w programowaniu mobilnym jest stosowany na wszystkich poziomach: od architektury aplikacji po pojedyncze klasy. W projektach Android Clean Architecture dzieli kod na trzy warstwy: domain (logika biznesowa — niezależna od frameworków), data (repozytoria, API, BD) i presentation (UI, ViewModel). Warstwa domain korzysta z zasad SOLID: use case (SRP), interfejsy repozytoriów (DIP), klasy encji (OCP + LSP).

Według Android Developers Guide, 2025, SRP w Android przejawia się w podziale na ViewModel, Repository i Mapper. OCP — przy dodawaniu nowych źródeł danych przez interfejs DataSource. LSP — w jednolitej obsłudze Result z różnych repozytoriów. ISP — w podejściu CQRS (podział repozytoriów Read/Write). DIP — przez Hilt/Koin do wstrzykiwania zależności.

ZasadaProblem bez niejRozwiązanie w projekcie mobilnym
SRPActivity na 1000+ liniiViewModel + UseCase + Repository
OCPswitch-case według typu płatnościStrategy: interfejs PaymentGateway
LSPBłąd przy zamianie BaseViewModelSprawdzenie kontraktu podklas
ISPUnsupportedOperationExceptionPodział Reader / Writer
DIPViewModel ręcznie tworzy RetrofitKontener DI Hilt / Koin

Typowe błędy przy stosowaniu SOLID

Błędy SOLID najczęściej wynikają z nadmiernego komplikowania kodu. Pierwszy — dosłowne trzymanie się zasad bez uwzględnienia kontekstu. Podzielenie jednej klasy UserService na 10 interfejsów i 15 klas dla „czystego” ISP to overengineering. SOLID to narzędzie, a nie cel. Drugi błąd — mylenie SRP z „jedna metoda = jedna odpowiedzialność”. Klasa może mieć wiele metod, jeśli wszystkie należą do jednego obszaru odpowiedzialności.

Według Simple Thread, 2024, trzeci błąd — ignorowanie LSP przy dziedziczeniu ViewModel w Android. Jeśli bazowa ViewModel oczekuje LiveData, a podrzędna używa StateFlow — kod kliencki subskrybujący LiveData nie otrzyma aktualizacji. Czwarty — naruszenie DIP dla testowania: RepositoryImpl bezpośrednio tworzy instancję OkHttpClient, co uniemożliwia testy jednostkowe.

Zasada złotego środka: stosuj SOLID, gdy rozwiązuje to realny problem (częste zmiany, trudność testowania, powielanie). Dla prostych ekranów CRUD ścisłe przestrzeganie wszystkich pięciu zasad jest przesadą. Dla logiki biznesowej, obliczeń finansowych i interakcji API SOLID jest obowiązkowy.

Związek SOLID z Clean Architecture

Clean Architecture (Robert C. Martin, 2012) — bezpośrednie zastosowanie SOLID na poziomie warstw aplikacji. SRP określa granice use case (każdy use case — jedna klasa). OCP jest realizowane przez interfejsy repozytoriów (Warstwa Data może się zmieniać bez zmiany Domain). ISP daje podział Use Case na boundary wejścia/wyjścia. DIP — kierunek zależności do wewnątrz warstwy Domain. LSP gwarantuje, że każda implementacja repozytorium jest wymienna bez psucia use case.

Często zadawane pytania

Czym jest SOLID prostymi słowami?

SOLID — pięć zasad pisania kodu, aby był łatwy do zmiany, testowania i zrozumienia. Każda litera to jedna zasada: nie pisz dużych klas (SRP), nie zmieniaj istniejącego kodu — dodawaj nowy (OCP), nie psuj zachowania dziedziczących (LSP) i inne.

Która zasada SOLID jest najważniejsza?

SRP (Single Responsibility) jest uważana za najważniejszą, ponieważ jej naruszenie prowadzi do God-klas — ogromnych klas, które są trudne do testowania i modyfikowania. Jednak bez DIP (Dependency Inversion) kod pozostaje silnie powiązany, co również jest krytyczne.

Czy SOLID jest obowiązkowy w programowaniu mobilnym?

Nie jest obowiązkowy, ale wysoce zalecany w projektach komercyjnych z długim cyklem życia. Dla prostych aplikacji (jeden ekran, brak logiki biznesowej) SOLID może być przesadny. Dla projektów z 50+ ekranami i 3+ programistami SOLID to niezbędne minimum.

Co się stanie, jeśli nie będę przestrzegać SOLID?

Konsekwencje: klasy stają się „grube” (1000+ linii), zmiana w jednym miejscu psuje trzy inne, niemożliwe jest pisanie testów jednostkowych, dodanie nowej funkcji zajmuje tygodnie zamiast dni. Z czasem kod zamienia się w „Big Ball of Mud” — zagmatwany i kruchy.

Jak sprawdzić, czy SOLID jest przestrzegany w projekcie?

Objawy przestrzegania: każda klasa ma mniej niż 200 linii, zmiana funkcji nie dotyka 5+ plików, testy pisze się bez mockowania 10 zależności, nowy programista rozumie strukturę w jeden dzień. Narzędzia takie jak SonarQube i detekt pomagają wykryć naruszenia SRP i DIP.

Podsumowanie

  • SOLID — pięć zasad OOP (SRP, OCP, LSP, ISP, DIP) do tworzenia elastycznego i łatwego w utrzymaniu kodu
  • SRP — każda encja odpowiada za jedno zadanie, rozwiązuje problem God-klas
  • OCP — rozszerzanie przez polimorfizm, a nie modyfikację istniejącego kodu
  • LSP — dziedziczące nie powinny psuć zachowania klasy bazowej
  • ISP — wąskie interfejsy zamiast uniwersalnych „szwajcarskich scyzoryków”
  • DIP — zależność od abstrakcji, wstrzykiwanie przez Hilt/Koin w Android
  • SOLID jest obowiązkowy dla Clean Architecture i komercyjnych projektów mobilnych

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również