Konstrukce, objektové paradigma, SRP, LSP, DRY, refactoring

BI-WI.21-13
  • Konstrukce,
  • objektové paradigma,
  • základní pravidla návrhu (SRP, LSP, DRY),
  • refactoring (příznaky a vybrané techniky).

Konstrukce SW

  • Konstrukce je fáze, kde se abstraktní návrh setkává s konkrétní implementací.

  • Následuje po analýze požadavků a návrhu

  • Proces kódování, ladění a testování

  • Transformace SW návrhu na spustitelný kód a integraci komponent do funkčního celku

  • Zahrnuje návrh v malém - detailní rozhodnutí která nebyla specifikována v předchozích fázích. Tato rozhodnutí musí být v souladu s celkovou architekturou a koncepcí systému.

  • Rozhodnutí během konstrukce:

    • např. volba algoritmů, specifických nastavení DMBS
    • přímo ovlivňují kvalitativní atributy SW.

Objektové paradigma (OOP)

  • Přístup k návrhu a programování, kde je systém dekomponován na objekty, které spolu navzájem komunikují.
  • Každý objekt nese vlastní data (atributy) a metody, které realizují odpovědnost za vykonání určité funkcionality.

Základní koncepty OOP

Třída (Class)

  • Představuje šablonu/blueprint podle které se vytvářejí objekty.

  • Definuje

    • Metadata atributů
    • Signatury metod
    • Implementace metod
    • Konstruktory objektu
  • Pro vizualizaci se často využívají UML diagramy tříd

Objekt

  • Konkrétní instance třídy

  • Hodnoty atributů objektu patří tomuto objektu (nejsou sdílené)

  • Metody objektu jsou společné pro všechny objekty dané třídy

  • Pro vizualizaci konkrétních instancí a jejich vztahů v daném okamžiku slouží UML objektové diagramy, kde jsou klíčové modifikátory přístupu pro znázornění zapouzdření.

../../Attachments/Pasted image 20260619151007.png

Zapouzdření

  • Princip skrývání vnitřního stavu objektu a implementačních detailů.
  • Přístup k datům objektu a jejich modifikace jsou možné pouze prostřednictvím veřejně dostupných metod.
  • Takto jsou data chráněna před nekonzistentním využitím jiným programátorem.
  • Umožňuje měnit vnitřní implementaci objektu bez dopadu na klienty využívající veřejné rozhraní.

Dědičnost

  • Umožňuje třídě přebírat atributy a metody z existující třídy.
  • Modeluje vztah “je typu” (is-a)
  • Podtřída může přidávat nové atributy a metody, nebo překrýt (”override”) zděděné metody (specializace).
  • V UML je dědičnost plnou čárou s zavřenou nevyplněnou šipkou od podtřídy k nadtřídě

Polymorfismus

  • Schopnost objektů různých “příbuzných” tříd reagovat na stejnou metodu různým/specializovaným způsobem.
  • Při volání metody je implementace metody určena typem objektu, ne typem proměnné

Abstraktní třída

  • Třída od které nelze vytvářet instance.
  • Abstraktní metody obsahují pouze jejich kontrakty, některé i implementaci.
  • Podtřídy musí buď implementovat všechny abstraktní metody, nebo být samy abstraktní.

Interface

  • Definuje pouze kontrakt bez žádné implementace.
  • Podtřídy pouze implementují rozhraní.

Základní pravidla návrhu

  • Tyto dvě pravidla jsou součástí principu SOLID:

    • SRP (Single Responsibility Principle)
    • O
    • LSP (Liskov Substitution principle)
    • I
    • D
  • Další pravidla:

    • DRY (Don't Repeat Yourself)
    • Programování proti rozhraní
    • Composition over inheritance
    • Don't talk to strangers

../../Attachments/Pasted image 20260620095153.png

SRP (Single Responsibility Principle)

  • Každá třída by měla mít pouze jednu odpovědnost a tedy pouze jeden důvod ke změně.

  • Každá metoda by měla mít právě jednu přesně definovanou zodpovědnost.

  • Zvyšuje soudržnost tříd (cohesion = míra, do jaké spolu zodpovědnosti jedné třídy souvisejí),

  • Snižuje provázanosti (coupling = měřítko toho, jak moc je jedna třída závislá na jiné) s ostatními částmi systému

  • Zlepšení čitelnosti, testovatelnosti a udržitelnosti kódu.

  • Změny týkající se jedné odpovědnosti by neměly ovlivnit jiné, nesouvisející odpovědnosti.

  • Příklad:

    • Třída UserSettings, která původně spravovala jak aktualizaci uživatelských nastavení, tak ověřování uživatele pomocí SMS, porušovala SRP.
    • Refaktorováním byla rozdělena na třídu UserSettings (pro správu nastavení) a UserAuth (pro SMS potvrzení), kde každá třída má nyní právě jednu odpovědnost.
  • Velký počet veřejných metod ve třídě může signalizovat, že třída má příliš mnoho odpovědností.

LSP (Liskov Substitution principle)

  • Objekty podtřídy musí být zaměnitelné za objekty nadtřídy, aniž by to porušilo správnost nebo očekávané chování programu.

  • Rozhodně bychom neměli používat dědičnost jenom proto, že obě třídy mají určitou množinu společných atributů

  • Zjednodušeně řečeno: "Co funguje pro předka, musí zaručeně a bez překvapení fungovat i pro jeho potomka."

  • Zajišťuje, že dědičnost je využívána korektně z hlediska chování.

  • Podtřídy by měly pouze rozšiřovat funkcionalitu nadtřídy, nikoli měnit invarianty kontraktu.

  • Příklad:

    • Matematicky je čtverec obdélník. Může tedy třída Ctverec dědit od třídy Obdelnik?
    • Třída Obdelnik má metody setSirka(int s) a setVyska(int v)
    • Pokud od ní zdědíme Ctverec, musíme tyto metody přepsat tak, aby vždy nastavovaly obě strany na stejnou hodnotu (aby to zůstal čtverec).
    • Porušení LSP:
      • Někde v systému máme metodu, která dostane obecný Obdelnik, nastaví mu šířku na 5 a výšku na 10 a očekává, že obsah bude 50.
      • Pokud jí ale podstrčíme instanci třídy Ctverec, ta si po zavolání setVyska(10) změní i šířku na 10. Obsah vyjde 100. Program se zachová nečekaně.

DRY (Don't Repeat Yourself)

  • Zatímco SRP a LSP jsou objektové principy, DRY je obecná programátorská zásada

  • Každá část znalosti (kódová logika, data, konfigurace) by měla mít v systému jedinou, jednoznačnou a autoritativní reprezentaci

  • Cílem je minimalizovat duplicitu informací nebo logiky.

  • Snížení duplicity snižuje riziko nekonzistencí při provádění změn. Změna se provede na jednom místě a propíše se do všech míst.

  • Příklad:

    • Špatně (WET - Write Everything Twice): Při registraci uživatele kontrolujeme, jestli heslo obsahuje číslo a má 8 znaků pomocí regulárního výrazu. Při změně hesla v profilu to kontrolujeme znovu stejným výrazem. Při resetování zapomenutého hesla zase.
    • Správně (DRY - Don't Repeat Yourself): Vytvoříme jednu metodu PasswordValidator.isValid(String password). Zbytek aplikace volá pouze tuto metodu.

Programování proti rozhraní

  • Tento princip říká, že když spolu dva objekty komunikují, neměly by se spoléhat na konkrétní třídu toho druhého, ale na obecný kontrakt (rozhraní / interface), který daný objekt splňuje.

  • Zvyšuje flexibilitu, protože umožňuje snadnou výměnu konkrétních implementací bez nutnosti významných změn.

  • Špatně (Proti implementaci): Všude v kódu e-shopu máš natvrdo vytvořenou třídu TwilioSmsService SMS = new TwilioSmsService();. Za rok se vedení rozhodne, že Twilio je moc drahé a přecházíte na InfoBip. Musíš projít celý e-shop a přepsat TwilioSmsService na InfoBipSmsService.

  • Dobře (Proti rozhraní): Vytvoříš rozhraní SmsService s metodou posliSMS(). Třídy TwilioSmsService a InfoBipSmsService toto rozhraní implementují. V kódu e-shopu pak voláš pouze obecnou SmsService. Výměna poskytovatele znamená změnu na jediném místě (v nastavení aplikace), zbytek kódu zůstává netknutý.

Composition over inheritance

  • Tento princip radí, že pokud chceš rozšířit chování nějaké třídy, je obvykle lepší vložit do ní jiný objekt jako její součást (kompozice), než z dané třídy vytvořit potomka (dědičnost).
  • Zkráceně: Vztah "má něco" (Has-A) bývá v architektuře bezpečnější než vztah "je něčím" (Is-A).

Dont talk to strangers (Law of Demeter)

  • Metoda objektu by měla volat pouze metody vlastní, svých atributů, objektů vytvořených v rámci dané metody, nebo objektů předaných jako parametry.

  • Cílem je omezení řetězení volání metod přes několik objektů. object.getA().getB().doSomething()

  • Špatně (Porušení zákona Demeter): zakaznik.getPenezenka().getKreditniKarta().strhniPenize(castka); Tímto kódem jsi doslova vlezl zákazníkovi do kapsy, vytáhl peněženku, z ní kartu a provedl platbu. Tvůj kód teď musí vědět, že zákazník má peněženku a že peněženka obsahuje kartu. Pokud zákazník přejde na Apple Pay (nebude mít peněženku), tvůj kód selže.

  • Dobře (Dodržení zákona Demeter): zakaznik.zaplat(castka); Zavoláš metodu na svém přímém sousedovi (zakaznik). Jak to zákazník udělá (jestli vytáhne hotovost, kartu, nebo zaplatí telefonem), je čistě jeho interní věc. Tvůj kód je elegantní a chráněný před změnami uvnitř třídy Zákazník.

Refactoring

  • Změna vnitřní struktury kódu, bez změny jeho vnějšího chování
  • Změny prováděny v malých krocích
  • Nutná existence testů, jinak není možné prokázat, že nedošlo ke změně vnějšího chování

Příznaky nutnosti refactoringu (code smells)

  • Duplicity

  • Dlouhé metody

  • Velké třídy - nízká soudržnost

  • Příliš mnoho parametrů

  • Komentáře

  • Složité struktury podmínek

  • Nedodržené jmenné konvence

  • Feature envy - metoda více využívá data jiné třídy, než své vlastní

  • Switch statements - často lze nahradit polymorfismem

  • Message chains - dlouhé řetězení volání metod

Techniky refactoringu

  • Přejmenování metody
  • Zapouzdření atributů (gettery, příp. settery)
  • Nahrazení dědičnosti kompozicí
  • Nahrazení chybového kódu za výjimky
  • Extrahování metody/kódu z dlouhé metody
  • Skrytí metody
  • Zavedení objektu prázdné hodnoty místo null objektu
  • Zavedení objektu jako parametr místo více proměnných

Vytvořeno: 18. 6. 2026, 16:21
Poslední aktualizace: 20. 6. 2026, 14:45