Slovník pojmů

SOLID

SOLID dává užitečné otázky pro návrh změn. Neříká ale, že více tříd, interface a vrstev automaticky znamená kvalitnější software.

Stručná definice

Pět vodítek, ne pět rigidních zákonů.

Každé písmeno SOLID popisuje jiný druh návrhového problému: příliš mnoho důvodů ke změně, rozšiřování bez rizikových zásahů, porušený kontrakt potomka, příliš široké rozhraní a závislost business pravidel na detailech. Principy dávají společný jazyk pro code review a refaktoring.

Největší hodnotu mají při konkrétní změně: přidání dopravce, jiné platby nebo nového vstupu do aplikace. Pokud jednoduchý kód nemá rozumnou budoucí variantu ani skutečnou hranici, KISS a přímé řešení mohou být lepší než abstrakce předem.

Jaký problém řeší

Změna bez skrytých vedlejších dopadů

SOLID neposuzuje krásu diagramu. Pomáhá najít místa, kde změna jedné věci nečekaně rozbije jinou nebo kde třída skrývá příliš mnoho rozhodnutí.

  • objekt, který současně počítá cenu, ukládá objednávku a posílá e-mail
  • dlouhý řetězec if nebo switch, do něhož se při každé variantě sahá na více míst
  • potomek, který nedokáže bezpečně splnit očekávání základního typu
  • klient, jenž musí implementovat či znát metody, které nepotřebuje
  • aplikační pravidlo pevně svázané s HTTP klientem, frameworkem nebo konkrétní databází

Praktický příklad

Zahájení platby v checkoutu

Checkout dostane implementaci PaymentInitiator jako závislost. Výběr konkrétní platební metody je smysluplnou proměnlivostí; kontrakt proto není vytvořený jen kvůli názvu principu. Každá implementace vrací PaymentStart se stejným významem, takže checkout nemusí znát detaily API poskytovatele.

Pokud by platební metoda neuměla vrátit očekávaný stav a nutila checkout dělat speciální větev pro interní detail, kontrakt by nebyl dobře navržený. Lepší je upravit význam kontraktu nebo oddělit skutečně rozdílný use case než maskovat rozdíl dědičností.

PHP

interface PaymentInitiator
{
    public function start(Order $order): PaymentStart;
}

final class Checkout
{
    public function __construct(private PaymentInitiator $payment) {}

    public function beginPayment(Order $order): PaymentStart
    {
        return $this->payment->start($order);
    }
}

Pět principů

Každý princip odpovídá na jinou otázku

Principy se překrývají jen částečně. Jeden problém nelze vyřešit slepým použitím zkratky SOLID.

  1. S — Single Responsibility Má tato část kódu jednu soudržnou odpovědnost a jeden hlavní důvod ke změně?
  2. O — Open/Closed Lze novou variantu přidat přes existující stabilní kontrakt, aniž se bezdůvodně rozbije osvědčené chování?
  3. L — Liskov Substitution Zůstane chování správné, když je implementace zaměněna jinou implementací stejného kontraktu?
  4. I — Interface Segregation Je rozhraní malé podle potřeb konzumenta, nebo ho nutí záviset na cizích schopnostech?
  5. D — Dependency Inversion Závisí vysoká business pravidla na stabilním kontraktu místo na konkrétním technickém detailu?

Hlavní principy

Význam, příklad a častý omyl u každého písmena

Příklady popisují záměr. V konkrétním projektu může být nejmenší rozumná hranice větší nebo menší.

SRP — Single Responsibility Principle

Třída má mít jednu soudržnou odpovědnost, tedy jeden hlavní typ důvodu ke změně. OrderConfirmation může koordinovat potvrzení objednávky, zatímco tvorba PDF faktury patří jinam. Neznamená to „jedna metoda na třídu“ ani rozdělování každého řádku do vlastního objektu.

OCP — Open/Closed Principle

Kód má být otevřený rozšíření, ale uzavřený vůči zbytečnému narušení stabilního chování. Nový dopravce lze přidat implementací smysluplného kontraktu místo úprav větvení všude. Neznamená to, že existující kód se nikdy nesmí změnit; oprava špatné abstrakce je často správná.

LSP — Liskov Substitution Principle

Implementace kontraktu musí zachovat očekávání klienta: nepřidat přísnější předpoklady, nezrušit slíbený výsledek a neporušit invariant. Pokud metoda DeliveryPriceQuote slibuje cenu pro danou adresu, náhrada nemá náhodně vyžadovat interní ID skladu. Dědičnost sama o sobě LSP nezaručuje.

ISP — Interface Segregation Principle

Konzument nemá být nucen záviset na metodách, které nepotřebuje. Čtečka výdejních míst může vyžadovat jen findPoints(), ne celý administrátorský kontrakt s mazáním a synchronizací. ISP nevyžaduje interface s jedinou metodou úplně všude; rozdělení musí odpovídat rozdílným klientům.

DIP — Dependency Inversion Principle

Vysoká pravidla nemají záviset na nízkoúrovňových detailech; obě strany mají záviset na abstrakci. Use case může znát kontrakt pro uložení objednávky, zatímco SQL nebo ORM implementuje tento kontrakt. DIP není dependency injection: DI je běžný způsob, jak takovou závislost předat.

Praktický návrh

Platební metody bez předčasné abstrakce

Přidání platby kartou a převodem ukazuje, kdy principy mohou pomoci. Nevyžaduje ale vytvořit framework pro tři jednoduché řádky.

Odpovědnost
Checkout rozhodne, zda lze objednávku zahájit. Konkrétní platební brána vytvoří svůj požadavek; nemá zároveň rozhodovat o expedici objednávky.
Kontrakt
PaymentInitiator vyjadřuje pouze schopnost zahájit platbu. Každá implementace vrátí výsledek, s nímž checkout umí pracovat.
Zaměnitelnost
Kartová brána i bankovní převod musí zachovat význam výsledku, například URL pro pokračování nebo informaci o dokončené platbě.
Složení
Konkrétní implementace se vybere na okraji aplikace podle zvolené metody. Checkout nemusí vytvářet HTTP klienta ani znát API každého poskytovatele.

Výhody a omezení

Dobrý návrh je čitelný i přiměřený

Přínosy

  • jasnější umístění odpovědnosti a menší riziko vedlejších změn
  • kontrakty, které lze cíleně testovat nebo nahradit
  • lepší podpora proměnlivých částí, jako jsou dopravci a platební brány
  • konkrétní otázky pro review místo neurčitého „udělej to čistěji“

Omezení a časté chyby

  • interface pro každou třídu bez druhého klienta nebo reálného kontraktu
  • předčasná strategie pro variantu, která zatím neexistuje
  • dědičnost použitá jen pro sdílení kódu, i když význam kontraktu nesedí
  • použití SOLID jako argumentu proti jednoduchému a srozumitelnému řešení

Pragmatičnost

Abstrakce má následovat proměnlivost a riziko, ne poučku.

Pokud projekt má právě jednu jednoduchou implementaci a žádný konkrétní důvod k výměně, přímá třída může být čitelnější než sada interface, factory a adapterů. Signálem pro změnu není počet řádků, ale opakované podmínky, různá očekávání klientů, obtížné testování nebo historie změn na stejném místě.

SOLID nevyžaduje konkrétní počet vrstev ani balíčků. Principy fungují jako hypotézy: pokud nová hranice zjednoduší změnu a zachová srozumitelnost, je užitečná. Pokud jen rozmnoží názvy a přesměrování, je rozumné ji odstranit.

Na co myslet

Při změně ověřit skutečný kontrakt a cenu abstrakce

Zdravé použití principů stojí na pozorovatelném chování, ne na počtu vytvořených souborů.

  • pojmenovat, kdo a proč bude odpovědnost měnit
  • testovat veřejné chování kontraktu i s alternativní implementací
  • nezpřísnit požadavky náhrady oproti deklarovanému kontraktu
  • rozdělit rozhraní podle potřeb jeho konzumentů, ne mechanicky podle metod
  • držet technické detaily na hranici aplikačního pravidla
  • při každé abstrakci umět vysvětlit, jakou dnešní nebo očekávanou změnu zjednodušuje

Časté otázky

SOLID bez zbytečných dogmat

Musí mít každá třída jen jednu metodu kvůli SRP?

Ne. SRP mluví o soudržné odpovědnosti a důvodu ke změně, ne o počtu metod. Třída může mít několik metod, pokud společně vyjadřují jeden srozumitelný celek.

Znamená Open/Closed, že se nesmí měnit existující kód?

Ne. Princip varuje před zbytečným narušováním stabilního chování při přidání varianty. Chybný nebo příliš úzký kód je často potřeba upravit; není účelné jej obcházet další vrstvou.

Je Dependency Inversion totéž co Dependency Injection?

Ne. DIP je princip směru závislostí k abstrakci. Dependency injection je technika předání objektu, která může DIP podpořit, ale lze ji použít i bez vhodné architektonické hranice.

Kdy SOLID nepoužívat?

Nejde o zapínanou technologii. U malého a stabilního kódu může být nejrozumnější nepřidávat žádnou novou abstrakci. Principy použijte jako otázky tehdy, když existuje konkrétní změna nebo problém se závislostmi.

Jak přistupuji k architektuře

Kód má chránit změnitelná pravidla a zůstat čitelný pro tým.

Při návrhu rozlišuji stabilní pravidla od technických detailů a hledám jen takové hranice, které se vyplatí vzhledem ke složitosti aplikace.

Zavolejte mi

Zavolám vám následující pracovní den mezi 9:00 a 17:00.

Můžete mi také zavolat rovnou.

+420 605 181 728

Nechte mi telefonní číslo a pošlete žádost o zpětné zavolání.

Odesláním souhlasíte se zpracováním údajů pro vyřízení žádosti.