🛠️ BI-SWI – Softwarové inženýrství

Státnicové poznámky · Bakalářské státní zkoušky · FIT ČVUT

3 státnicové okruhy UML diagramy Návrhové vzory Refactoring
Okruh 1

Modelování obchodních procesů, analytický doménový model, analýza a správa požadavků

Pokrývá diagram aktivit UML pro modelování procesů, UML diagram tříd a stavový diagram pro doménový model, a kompletní analýzu požadavků včetně diagramu případů užití a scénářů.

🔄 Modelování obchodních procesů – účel a přínosy

Obchodní proces je sada uspořádaných činností/akcí, která transformuje vstupy na výstupy. K procesu patří role zodpovědné za jednotlivé činnosti.

Než začneme navrhovat software, musíme pochopit, co zákazník aktuálně dělá a jak. Modelování procesů není jen technická formalita – je to způsob, jak odhalit problémy ještě před zahájením vývoje.

  • Popsání činnosti zákazníka – pochopení jeho potřeb a problémů z pohledu praxe
  • Přesnější specifikace požadavků – víme, co systém musí podporovat
  • Lepší podpora procesů v navržené aplikaci
  • Zlepšení samotných procesů – bez ohledu na implementaci (re-engineering)
  • Identifikace „problémových" míst – kde teče čas nebo vznikají chyby
⚠️ Proč nestačí mluvit jen s manažerem?

Manažer vidí procesy idealizovaně. Budoucí uživatelé systému mají různé role a jejich problémy se zásadně liší. Je třeba znát způsob práce všech rolí, ideálně je pozorovat přímo při práci.

Typy modelů procesů:

  • AS IS – současný stav procesů, bez ohledu na navrhovaný systém. Slouží k pochopení zákazníka, nalezení problémů a jako podklad pro nové procesy.
  • TO BE – stav po realizaci nového systému. Umožňuje srovnání a vyhodnocení přínosů.

Notace pro modelování procesů:

  • Eriksson-Penker – rozšíření UML pro podnikové procesy
  • BPMN (Business Process Model and Notation) – průmyslový standard, bohatá notace
  • Diagram aktivit UML – nejčastěji používaný ve výuce FIT ČVUT

Způsob zachycení: Text je nejdůležitější. Diagram slouží pro snadnější pochopení a ověření. Sledujeme důležitost procesu, četnost provádění a časovou/finanční náročnost.

📊 UML Diagram aktivit

Diagram aktivit patří do skupiny UML diagramů chování. Slouží pro modelování obchodních procesů a vývojové diagramy algoritmů. V Enterprise Architect: UML Behavioral – Activity.

Diagram aktivit zobrazuje tok řízení – jak se systém nebo uživatel pohybuje od jedné akce k druhé, jak se tok větví a jak probíhají paralelní činnosti.

Stavební kameny diagramu aktivit:

Akční uzly – reprezentují činnosti, které se provádějí:

  • Akce – základní atomická činnost (zaokrouhlený obdélník)
  • Akce spouštějící aktivitu – volá jinou aktivitu jako podproces
  • Odeslání události – posílá signál jinému účastníkovi (pentagon s šipkou ven)
  • Přijetí události – čeká na příchod signálu (pentagon se šipkou dovnitř)
  • Časová událost – akce spuštěná časovačem (přesýpací hodiny)

Řídící uzly – řídí tok:

  • Počáteční uzel – plný černý kruh, začátek aktivity
  • Koncový uzel aktivity – plný kruh v kroužku, ukončí celou aktivitu
  • Konec toku – křížek v kroužku, ukončí pouze jeden tok (ostatní pokračují)
  • Rozhodovací uzel (Decision) – diamant, tok se větví na základě podmínky. Podmínky na větvích musí být výlučné – vždy splněna právě jedna!
  • Slučovací uzel (Merge) – diamant, sbíhá se více toků do jednoho (bez synchronizace)
  • Paralelní Fork – černá tlustá čára, rozdělí jeden tok do více paralelních
  • Paralelní Join – černá tlustá čára, počká na dokončení VŠECH paralelních větví
❌ Typická chyba: Záměna Merge a Join

Merge (slučovací uzel) slouží pro sbíhání alternativních toků – propustí první příchozí token. Nezajišťuje synchronizaci. Join (synchronizační uzel) čeká na VŠECHNY paralelní větve. Používá stejnou grafiku (tučná čára), ale sémantika je odlišná. Pokud chceme zachytit cyklus: slučovací uzel nesmíme nahradit přímým zpětným tokem do akce – musíme použít explicitní Merge uzel.

Další prvky diagramu aktivit:

  • Zóny zodpovědnosti (swimlanes) – rozdělení diagramu do oblastí odpovídajících rolím nebo systémům. Akce v dané zóně provádí daná role.
  • Objektové uzly – zachycují stav objektu přenášeného mezi akcemi (obdélník s názvem třídy, volitelně stav v hranatých závorkách, např. Výtisk [Vypůjčen])
  • Vícenásobné provedení akce (Expansion Region) – akce se provede pro každý prvek kolekce
  • Přerušení provádění (Interrupt Region) – region, jehož zpracování lze přerušit výjimečnou událostí; všechny tokeny uvnitř jsou odstraněny
✅ Jak číst průchody větvením

Příklad: uzly A → Decision → B nebo C → D. Možné průchody jsou: A, B, D a A, C, D. U paralelního fork/join: E → Fork → (F a G paralelně) → Join → H. Průchody: E,F,G,H nebo E,G,F,H (pořadí F a G závisí na prostředí, ale H nastane až po obou).

Typická chyba č. 2 – Míchání stavů objektů mezi akce:

Stav objektu (např. „Vypůjčen") nesmí figurovat jako akce v diagramu. Akce způsobují změnu stavu. Správně: přidáme objektový uzel s daným stavem a akci, která stav vyvolá (např. „Zapsat výpůjčku" → Výtisk [Vypůjčen]).

📋 Analýza a správa požadavků

Požadavek je popis chování, vlastnosti nebo omezení, které musí systém splňovat. Cílem analýzy požadavků je vymezit hranice systému, umožnit přesnější odhad pracnosti, vyjasnit zadání se zákazníkem a zachytit omezení kladená na IS.

Kategorizace požadavků:

  • Projektové požadavky – cena, termíny, školení, způsob dodání
  • Produktové požadavky – Funkční – popisují chování systému (co systém dělá). Příklad: „Systém bude evidovat knihy a výtisky."
  • Produktové požadavky – Obecné / Nefunkční – určují omezení kladená na systém. Mají zásadní dopad na architekturu. Příklad: „Aplikace zvládne 100 souběžných uživatelů."

Klasifikace nefunkčních požadavků – FURPS:

PísmenoOblastPříklad požadavku
F – FunctionalityFunkčnostSystém eviduje výpůjčky
U – UsabilityPoužitelnostNový uživatel zvládne práci do 30 minut
R – ReliabilitySpolehlivostDostupnost 99,9 % (max. 8,7 h výpadku/rok)
P – PerformanceVýkonOdezva do 2 sekund pro 100 uživatelů
S – SupportabilityPodporovatelnost / rozšiřitelnostSystém bude rozšiřitelný o nové knihovny

Evidované informace o požadavku:

  • Název a zkratka (usnadňuje odkazování, např. F1, N2)
  • Popis – nejdůležitější část, musí být jednoznačný
  • Typ (kategorie), priorita, složitost
✅ Vlastnosti správného požadavku

Každý požadavek musí být: Jednoznačný (ne „systém bude výkonný"), Splnitelný a Ověřitelný (splnění musí jít prokázat v rámci akceptačního testování). Příklady špatných požadavků: „Systém bude přívětivý", „Systém bude spolehlivý".

Způsoby zachycení požadavků:

  • Strukturovaný text – přirozený jazyk s pevnou šablonou
  • Uživatelské příběhy (User Stories) – psány z pohledu uživatele, typické pro agilní vývoj. Nesnaží se popsat kompletní chování celého systému.
  • Use Case model – detailní zachycení funkčních požadavků

👤 Model případů užití (Use Case Model)

Model případů užití je primárně textový popis funkčních požadavků. Funkčnosti jsou popsány z pohledu interakce uživatele/aktéra se systémem. Diagramy jsou pouze „doplňkem" pro snadnější orientaci.

Využití: vyjasnění požadavků se zákazníkem, základ pro uživatelskou příručku, podklady pro akceptační testy, zpřesnění odhadů pracnosti, zadání pro programátora.

Složení modelu případů užití:

  • Seznam aktérů – každý aktér je popsán (kdo je, jakou roli zastává)
  • Diagramy případů užití – grafický přehled
  • Seznam případů užití – každý případ užití obsahuje název, zkratku, popis (cíl), scénáře (hlavní, alternativní, výjimky) a podmínky provedení

Aktér:

  • Role, která má zájem využívat funkčnosti systému pro dosažení cíle
  • Vždy se jedná o externí entitu – mimo modelovaný systém
  • Jedna konkrétní osoba může zastávat více rolí současně
  • Speciální aktér: Čas – automaticky spouštěné úlohy na základě časové události
  • Mezi aktéry lze použít generalizaci/dědičnost – potomek může spouštět i všechny případy užití svého rodiče

UML Diagram případů užití:

  • Patří do skupiny diagramů chování (UML Behavioral – Use Case)
  • Aktér – panáček (stickman) nebo obdélník se stereotypem
  • Případ užití – elipsa s názvem uvnitř hranice systému
  • Hranice systému – obdélník ohraničující všechny případy užití
  • Asociace – čára mezi aktérem a případem užití
  • «include» – povinná součást, vždy se provede; vyčleňuje sdílené části scénářů (přerušovaná šipka od UC k inkludovanému UC)
  • «extend» – volitelné rozšíření; nepovinná část, provede se jen za určitých podmínek (přerušovaná šipka od rozšíření k základnímu UC)
❌ Nejčastější chyby v UC diagramu
  • Diagram případu užití NEZNÁZORŇUJE tok událostí – k tomu slouží textové scénáře nebo diagram aktivit
  • Diagram NEZNÁZORŇUJE datová úložiště (databáze, soubory)
  • Případ užití nevyužívaný žádným aktérem nemá smysl (nebo chybí aktér)
  • Zachycení činností prováděných MIMO systém – ty patří do modelu obchodních procesů

Scénáře případů užití:

  • Hlavní scénář – standardní průchod bez chyb a výjimek. Číslovaný seznam kroků (střídají se aktér a systém). Příklad: 1. Aktér klikne, 2. Systém zobrazí formulář, 3. Aktér vyplní…
  • Alternativní scénář – odlišný průchod existující část scénáře (např. jiný způsob vložení fotografie)
  • Výjimky – chybové stavy (např. nevalidní ISBN)
  • Podmínky provedení – vstupní (pre-condition) a výstupní (post-condition)

Granularita případů užití:

  • Jeden scénář cca 10 kroků
  • Umožňuje uživateli splnit konkrétní cíl, jehož provedení trvá řádově hodiny
  • Případ užití musí mít přínos pro uživatele, který ho provádí
💡 Doporučení pro psaní scénářů
  • Popisujte „co" musí systém dělat, vyhněte se popisu „jak" to bude dělat
  • Detailní scénáře pište pouze u „zajímavých" UC
  • Nepřepisujte neustále stejné jednoduché kroky do každého UC (použijte «include»)
  • Pokud je scénář složitý (více alternativních toků), doplňte diagram aktivit
  • Pochopení UC velmi usnadňuje grafický návrh obrazovky (wireframe)

🗂️ Analytický doménový model – UML diagram tříd

Doménový model (analytický) popisuje konceptuální třídy z problémové domény zákazníka – entity, jejich atributy a vazby. Nejde o softwarový model! V UML se zaznamenává jako diagram tříd (UML Structural – Class).

Cíle: popsat data a jejich meaning (slovníček pojmů), popsat vazby mezi entitami, identifikovat stavy entit, poskytnout základ pro návrh (databázový model, návrhový model tříd).

UML Diagram tříd – základní prvky:

  • Třída – obdélník rozdělený na tři části: název, atributy (název:typ), metody
  • Viditelnost: - private, # protected, + public
  • Atribut vs. asociace – v analytickém modelu preferujeme asociaci (vazbu), protože je názornější a vyjadřuje relaci mezi entitami. Sémanticky jsou ekvivalentní.

Typy vztahů:

VztahSymbolPopisPříklad
AsociaceČáraObecná vazba mezi třídamiČtenář – Výpůjčka
KompoziceVyplněný diamantSilná celek-část: část bez celku neexistujeObjednávka – Položka objednávky
AgregacePrázdný diamantSlabší celek-část: část může existovat samostatněTým – Hráč (doporučuje se nepoužívat)
Generalizace / DědičnostŠipka s prázdnou hlavouIs-a vztah: potomek je speciálním případem rodičeKnihovník → Uživatel

Násobnosti (multiplicity):

  • 1 – přesně jeden
  • 0..1 – nula nebo jeden (volitelný)
  • 0..* nebo * – nula nebo více
  • 1..* – jeden nebo více (alespoň jeden)

Jak hledat třídy:

  • Předměty a objekty reálného světa
  • Podstatná jména z vytvořených dokumentů (procesní model, UC model, slovníček)
  • Kategorie: obchodní transakce (výpůjčka, platba), položky transakcí, produkty a služby (kniha), role a aktéři, fyzické objekty (výtisk), popisy věcí
❌ Typické chyby v doménovém modelu
  • Třída obsahuje atributy specifické pro implementaci (getter, setter, id jako primární klíč)
  • Doménový model obsahuje softwarové třídy (Controller, DAO, Service) – ne!
  • Třída obsahuje cizí klíče místo asociací
  • Nevhodné použití dědičnosti – pokud objekt může měnit svůj typ/kategorii, dědičnost nevhodná (místo toho atribut typ)
  • Sloučení atributů popisujících instanci a obecný popis do jedné třídy (Výtisk a Kniha musí být dvě třídy)

Asociační třída: Pokud má samotná vazba atributy (např. vazba Výpůjčka mezi Čtenářem a Výtiskem má datum výpůjčky), zavede se asociační třída. Většinou si však vystačíme bez ní – stačí třída Výpůjčka s atributy a asociacemi.

🔀 UML Stavový diagram

Stavový diagram (State Machine Diagram) patří do skupiny UML diagramů chování. Modeluje konečný stavový automat pro konkrétní entitu/třídu – zachycuje možné stavy objektu a přechody mezi nimi. V Enterprise Architect: UML Behavioral – State Machine.

Cíle: porozumění životnímu cyklu entit, vyjasnění stavů, ve kterých se může entita nacházet, zachycení událostí vyvolávajících přechod a podmínek, za kterých může změna nastat.

Prvky stavového diagramu:

  • Počáteční pseudostav – plný černý kruh (odkud objekt začíná)
  • Koncový stav – kruh v kroužku
  • Stav – zaoblený obdélník s názvem stavu
  • Přechod – šipka mezi stavy s popisem ve formátu: Událost[Podmínka]/Akce

Syntax přechodu:

  • Událost – co přechod spouští (např. vrácenKnihovníkem)
  • Podmínka (guard) – v hranatých závorkách, musí být splněna (např. [dne není po termínu])
  • Akce – co se provede při přechodu (za lomítkem)
⚠️ Stavový diagram ≠ diagram aktivit!

Stavový diagram popisuje stavy jednoho objektu v čase a přechody mezi nimi. Nevznikají zde nové objekty ani se nezachycuje tok dat. Diagram aktivit popisuje tok řízení procesu – sekvenci akcí, větvení, paralelismus. Jsou to různé nástroje pro různé účely.

Příklad – stavový diagram Výtisku:

  • Stav Volný → přechod „PůjčenČtenáři" → stav Vypůjčen
  • Stav Vypůjčen → přechod „VrácenKnihovníkem" → stav Volný
  • Stav Volný → přechod „Vyřazen" → stav Vyřazen
Okruh 2

Vzory návrhu: třívrstvá architektura, MVC, GoF vzory, GRASP vzory, spolupráce objektů

Pokrývá architektonické vzory pro organizaci systému do vrstev, vzor MVC pro prezentační vrstvu, vybrané GoF vzory (Abstract Factory, State, Adapter), GRASP vzory (Low Coupling, High Cohesion) a UML sekvenční diagram pro zachycení spolupráce objektů.

🎯 GRASP vzory – principy přiřazení zodpovědností

GRASP (General Responsibility Assignment Software Patterns) jsou základní vzory/principy pro přiřazení zodpovědností třídám. Zodpovědnost je úkol, který má třída řešit (metody, které tato třída implementuje).

Existuje mnoho způsobů rozdělení úloh mezi třídy a neexistuje jediné správné řešení. GRASP nám dává vodítko, jak rozhodnout.

Informační expert (Information Expert):

Základní princip: Přiřaďte zodpovědnost třídě, která má informace potřebné pro splnění této zodpovědnosti. Proč? Protože tak se minimalizuje nutnost přístupu jiných tříd k datům – data jsou tam, kde se s nimi pracuje.

⚖️ Nízká provázanost vs. Vysoká soudržnost – klíčová rovnováha

Nízká provázanost (Low Coupling): Přiřaďte zodpovědnost tak, aby provázanost (počet vazeb mezi třídami) zůstala nízká. Každá třída by si měla co nejvíce vystačit sama. Nízká provázanost zvyšuje znovupoužitelnost a snižuje dopad změn.

Vysoká soudržnost (High Cohesion): Přiřaďte zodpovědnost tak, aby soudržnost třídy zůstala vysoká. Každá třída by měla být zaměřena na jeden úkol. Zvyšuje srozumitelnost systému – zodpovědnost třídy je snadno pochopitelná.

Paradox: „Jedna třída umí všechno" = nízká provázanost, ale nízká soudržnost. „Každá třída jen jedna metoda" = vysoká soudržnost, ale vysoká provázanost. Hledáme rovnováhu!

Ostatní GRASP vzory (pro přehled): Tvůrce (Creator), Řadič (Controller), Polymorfismus, Čistá výroba (Pure Fabrication), Nepřímý odkaz (Indirection), Chráněné variace (Protected Variations)

🏛️ Vícevrstvá (třívrstvá) architektura

Třívrstvá architektura rozděluje systém do tří logických vrstev s jasně definovanou zodpovědností: Prezentační vrstva, Business (doménová) vrstva a Datová vrstva. Závislost vede vždy shora dolů.

Evoluce architektur:

  • Monolitická (jednovrstvá) – vše smícháno dohromady (SQL v HTML šabloně). Vhodná pouze pro prototypy, složitá udržovatelnost.
  • Dvouvrstvá – oddělena prezentační vrstva od datové (DAO vrstva). Vhodná pro jednoduché CRUD aplikace.
  • Třívrstvá – přidána business vrstva. Vhodná pro enterprise aplikace.
  • Vícevrstvá – třívrstvá s dalším dělením na podsystémy.
VrstvaZodpovědnostPříklady
Prezentační Zobrazení dat uživateli, zpracování vstupu, navigace, formátování výstupů HTML stránky, šablony, REST API, GUI
Business (doménová) Business logika, procesy a validace. Nezávislá na prezentační i datové vrstvě. SpravceCtenaru, VypujckaService
Datová (technická) Persistence dat, přístup k databázi nebo externím službám CtenarDAO, HibernateRepository

Striktní vs. Relaxovaná třívrstvá architektura:

  • Striktní – závislost vždy směrem dolů a pouze o jednu úroveň (prezentační → business → datová). Čistější, ale méně flexibilní.
  • Relaxovaná – závislost vždy dolů, ale přes libovolný počet úrovní. V praxi nejvíce používaná.
✅ Výhody třívrstvé architektury
  • Oddělení business logiky od prezentace – logiku lze testovat bez GUI
  • Nezávislost business logiky na způsobu uložení dat – lze vyměnit DB technologii
  • Více různých prezentačních vrstev (webová, mobilní API, desktop)
  • Znovupoužitelnost: čím nižší vrstva, tím lze ji použít na více projektech (logování, persistence)
  • Jednoduché testování – mockování vrstev je přímočaré

🖥️ Model View Controller (MVC)

MVC je vzor pro řešení problémů prezentační vrstvy. Odděluje logiku zobrazení (View) od dat (Model) a od zpracování uživatelského vstupu (Controller).

Komponenty MVC:

  • Model – zapouzdřuje data a business logiku. Notifikuje View o změnách (vzor Observer). Dvě varianty:
    • Aktivní model – sám upozorní View na změny (Observer pattern)
    • Pasivní model – pouze reaguje na dotazy, Controller zajistí obnovení View
  • View – zobrazuje data z Modelu uživateli. Nezahrnuje business logiku. Může být více View pro jeden Model.
  • Controller – zpracovává uživatelský vstup (klik, formulář), volá metody Modelu a určuje, která View se zobrazí.

Vztah MVC a třívrstvé architektury:

MVC je vzor pro prezentační vrstvu třívrstvé architektury. Model v MVC volá Business vrstvu, Controller orchestruje. View a Controller jsou součástí prezentační vrstvy.

MVP (Model-View-Presenter): Varianta MVC pro GUI based na komponentách (Swing, WinForms). View odchytí událost, ale deleguje zpracování na Presenter. View implementuje rozhraní, které Presenter volá.

Příklady frameworků MVC/MVP:

  • Spring MVC, Grails – Java
  • JSF 2, MyFaces – Java EE (spíše MVP)
  • Angular, React – JavaScript (MVVM varianta)
  • ASP.NET MVC – .NET

🏭 GoF návrhové vzory

GoF vzory (Gang of Four) jsou 23 klasických návrhových vzorů popsaných v knize „Design Patterns" (Gamma, Helm, Johnson, Vlissides). Dělí se na vzory pro vytváření (Creational), strukturální (Structural) a vzory chování (Behavioral).

Abstraktní továrna (Abstract Factory) – Creational:

Řeší problém vytváření rodiny/varianty objektů. Klient pracuje pouze s rozhraním továrny a nemusí znát konkrétní třídy produktů.

  • AbstractFactory – rozhraní/abstraktní třída definující metody pro vytváření všech produktů
  • ConcreteFactory – konkrétní implementace pro danou variantu (např. TestFactoryDAO, HibernateFactoryDAO)
  • AbstractProduct – rozhraní produktu (ICtenarDAO, IVypujckaDAO)
  • ConcreteProduct – konkrétní implementace produktu

Proč to tak je? Při vývoji chceme testovat bez databáze (TestFactoryDAO vrátí mock objekty). V produkci použijeme HibernateFactoryDAO. Kód klienta se nezmění.

💡 Příklad z přednášky: DAO Factory

SpravceCtenaru dostane factory (buď TestFactory nebo HibernateFactory) a přes ni vytvoří ICtenarDAO a IVypujckaDAO. Ať už je factory jakákoliv, SpravceCtenaru funguje stejně.

Vzor Stav (State) – Behavioral:

Odděluje chování třídy závislé na stavu do samostatné třídy. Odstraňuje složitá větvení (switch/case, if/else) závislá na stavu objektu.

  • Context – třída, jejíž chování se mění v závislosti na stavu (např. Výtisk). Udržuje referenci na aktuální stav.
  • State – abstraktní třída/rozhraní pro všechny stavy
  • ConcreteState – konkrétní stavy (StavVolny, StavVypujcen) – každý implementuje jiné chování metod

Proč to tak je? Bez vzoru State by metoda zpracuj() obsahovala: if (stav == VOLNY) {...} else if (stav == VYPUJCEN) {...}. S přidáním nového stavu bychom museli modifikovat tuto metodu. Se vzorem State přidáme pouze novou třídu stavu.

Adaptér (Adapter) – Structural:

Konvertuje rozhraní jedné třídy na rozhraní jiné. Umožňuje propojit třídy s nekompatibilními rozhraními bez jejich modifikace.

  • Target – rozhraní, které klient očekává
  • Adaptee – existující třída s jiným rozhraním
  • Adapter – implementuje Target rozhraní a deleguje volání na Adaptee

Příklad z praxe: Naše aplikace očekává rozhraní IPlatebniSluzba. Integrujeme nový platební gateway, jehož API je zcela jiné. Napíšeme Adapter, který překládá volání z IPlatebniSluzba na API gateway.

Stavitel (Builder) – Creational:

  • Director – řídí strukturu (pořadí stavění) výsledného produktu
  • Builder – umí postavit jednotlivé části produktu v konkrétní „technologii"
  • Příklad z přednášky: SQLBuilder – Director řídí, co a v jakém pořadí se přidá do SQL scriptu; konkrétní Builder (OracleBuilder, PostgreSQLBuilder) to realizuje pro danou DB.

Pozorovatel (Observer) – Behavioral:

  • Pozorující objekty se zaregistrují u pozorovaného objektu
  • Musí implementovat požadované rozhraní (IObserver)
  • Při změně upozorní pozorovaný objekt všechny pozorující touto metodou
  • Využití: aktivní Model v MVC upozorňuje View na změny

📞 Spolupráce objektů – UML Sekvenční diagram

Sekvenční diagram patří do skupiny UML diagramů chování (UML Behavioral – Sequence). Zachycuje komunikaci spolupracujících objektů (instancí návrhových tříd) při realizaci scénáře případu užití. Klíčový nástroj pro přiřazení zodpovědností.

Prvky sekvenčního diagramu:

  • Lifeline (životní čára) – svislá přerušovaná čára znázorňující životnost objektu. Nahoře je obdélník s názvem objektu ve formátu nazev:Trida nebo anonymně :Trida.
  • Aktivační obdélník – úzký obdélník na životní čáře znázorňující dobu, kdy je objekt aktivní (vykonává kód)
  • Synchronní zpráva – plná šipka → , volající čeká na odpověď
  • Asynchronní zpráva – otevřená šipka → , volající nečeká
  • Návratová hodnota – přerušovaná šipka zpět, s názvem proměnné/hodnoty
  • Statická metoda – volání na třídě, ne na objektu (podtžený název třídy)
  • Vytvoření objektu – šipka míří na obdélník nového objektu
  • Zrušení objektu – X na konci životní čáry
  • Self message – šipka zpět na vlastní lifeline (volání vlastní metody)
  • Found message – zpráva s neznámým odesílatelem (začíná plným kruhem)

Kombinované fragmenty (framing):

  • alt – alternativa (if-else); každá větev má guard podmínku v hranatých závorkách
  • opt – volitelné (if bez else)
  • loop – cyklus; guard podmínka nebo loop(min, max)
  • par – paralelní provedení
  • ref – odkaz na jiný diagram

Iterace přes kolekci: Označuje se smyčkou, uvnitř je notace [foreach prvek : kolekce]

Okruh 3

Konstrukce, objektové paradigma, základní pravidla návrhu (SRP, LSP, DRY), refactoring

Pokrývá objektové programování (třída, objekt, zapouzdření, dědičnost, polymorfismus), principy správného návrhu kódu a techniky refactoringu pro udržování kvality zdrojového kódu.

🧩 Objektové paradigma

Objektové paradigma je přístup k programování, při kterém je systém dekomponován na objekty, které spolu navzájem komunikují. Každý objekt nese svá vlastní data (atributy) a metody (zodpovědnosti).

Základní pojmy:

Třída je šablona (blueprint), podle které se vytvářejí objekty. Definuje:

  • Metadata atributů (název, typ, viditelnost)
  • Signatury metod (název, parametry, návratový typ)
  • Implementace metod
  • Konstruktory pro inicializaci atributů při vytvoření objektu

Objekt (instance třídy):

  • Hodnoty atributů patří konkrétnímu objektu – nejsou sdílené mezi instancemi
  • Implementace metod je sdílená pro všechny objekty dané třídy

Čtyři pilíře OOP:

1. Zapouzdření (Encapsulation):

  • Objekt je „black box" – drží svůj stav uvnitř sebe sama
  • Implementační detaily jsou schovány za množinou veřejných metod
  • Přímý přístup k atributům je zakázán (private), k dispozici jsou gettery/settery
  • Proč? Umožňuje měnit vnitřní implementaci bez ovlivnění vnějšího kódu. Chrání před nekonzistentním stavem.

2. Dědičnost (Inheritance):

  • Podtřída (potomek) přebírá atributy a metody nadtřídy (rodiče)
  • Metody podtřídy mohou překrýt (override) metody nadtřídy
  • Ve většině jazyků pouze jednoduchá dědičnost (Java, C#)
  • Výhoda: kód rodiče se znovu použije, potomci přidávají specifika

Abstraktní třída:

  • Atributy a metody (i abstraktní – bez implementace) jsou povoleny
  • Konstruktory jsou povoleny, ale nelze je použít pro vytváření instancí
  • Podtřída musí implementovat všechny abstraktní metody, nebo musí být také abstraktní

3. Polymorfismus (Polymorphism):

  • Při volání metody je implementace určena typem objektu (dynamická vazba), ne typem referenční proměnné
  • Příklad: proměnná typu Zivocich zv = new Pes(). Volání zv.zvuk() zavolá metodu třídy Pes, ne Zivocich.
  • Proč? Umožňuje psát generický kód pracující s rozhraním – konkrétní chování dodají potomci.

4. Abstrakce (Abstraction):

  • Skrytí implementačních detailů za rozhraní nebo abstraktní třídu
  • Klient pracuje s abstraktem, nevidí konkrétní implementaci

UML objektový diagram: Patří do diagramů struktur. Zobrazuje konkrétní instance (objekty) a jejich aktuální stav (hodnoty atributů) v daném čase. Usnadňuje pochopení diagramu tříd – jde o „snímek" systému.

📐 Základní principy návrhu: SRP, LSP, DRY

SRP, LSP a DRY jsou principy, které vedou k čistému, udržovatelnému a rozšiřitelnému kódu. Jsou součástí SOLID principů a základů softwarového řemesla.

SRP – Single Responsibility Principle (Princip jediné zodpovědnosti):

Každá třída (ale i metoda) by měla mít právě jednu přesně definovanou zodpovědnost. Přeloženo: třída by se měla měnit pouze z jednoho důvodu.

  • Proč? Třída s jednou zodpovědností je snadněji pochopitelná, testovatelná a méně pravděpodobně se rozbije při změnách jiných částí systému.
  • Signál porušení SRP: prázdný řádek v těle metody nebo komentář v těle metody – indikuje, že metoda dělá více věcí (kandidát na extrahování)
  • Příklad porušení: třída Uzivatel obsahuje metody pro ověření, ukládání do DB i odesílání emailu.
  • Příklad splnění: samostatné třídy AutentizacniSluzba, UzivatelDAO, EmailService

LSP – Liskov Substitution Principle (Liskovův substituční princip):

Pokud instanci nadtřídy nahradíme instancí podtřídy, pak kód očekávající instanci nadtřídy by měl fungovat správně i s instancí podtřídy.

  • Proč? Dědičnost by měla být skutečně IS-A vztah, ne jen sdílení implementace. Porušení LSP způsobuje nečekané chyby při polymorfismu.
  • Klasický příklad porušení: Ctverec extends Obdelnik. Čtverec má vždy stejnou šířku a výšku. Pokud zavoláme na obdélník setSirka(2) a setVyska(3), očekáváme obsah 6. Ale pokud je to čtverec, setVyska(3) nastaví šířku i výšku na 3, obsah bude 9, ne 6. LSP je porušen.
  • Řešení: nesnažit se modelovat matematické vztahy přes dědičnost, pokud mají různé kontrakty
⚠️ Upřednostňování skládání před dědičností

Dědičnost je velmi silná vazba. Většina jazyků umožňuje pouze jednoduchou dědičnost. Vícenásobná dědičnost většinou porušuje vysokou soudržnost a způsobuje problémy (diamond problem). Příklady nevhodné dědičnosti: Fronta extends ZřetězenýSeznam, Bod extends Čtverec. Lepší řešení: kompozice (Fronta obsahuje ZřetězenýSeznam).

DRY – Don't Repeat Yourself (Neopakuj se):

Každý kus znalosti (logika, data, konfigurace) by měl mít v systému jednu jednoznačnou autoritativní reprezentaci.

  • Proč? Duplikace kódu je zdroj chyb – při opravě chyby musíme najít a opravit všechny kopie. Při přidání funkce musíme přidat na více místech. Snadno se opomene jedno místo.
  • DRY pro kód – zapouzdřit opakující se logiku do metody nebo třídy
  • DRY pro data – správný návrh způsobu ukládání dat (normalizace); používání pojmenovaných konstant místo „magic numbers"

Pozor: Ne vždy je copy-paste porušením DRY! Pokud dvě části kódu vypadají podobně, ale jsou logicky nezávislé a mohou se vyvíjet odděleně, spojování je předčasná abstrakce. Musíme rozlišit náhodnou shodu od skutečné duplikace logiky.

Don't talk to strangers (Zákon Démétry):

V metodě objektu je možné volat pouze:

  • Jiné metody tohoto objektu (this.metoda())
  • Metody objektů, které vlastní (atributy)
  • Metody objektů, které vytváří
  • Metody objektů, které dostala jako parametr

Porušení: order.getCustomer().getAddress().getCity() – volám metody „cizích" objektů přes řetězec. To zvyšuje provázanost.

Programování proti rozhraní:

  • Rozhraní (interface) je kontrakt: definuje, co objekt umí, ne jak to dělá
  • Rozhraní všude tam, kde jsou očekávány změny nebo rozšíření
  • Zveřejněné rozhraní by mělo být „neměnné" – klienti závisí na kontraktu, ne na implementaci
  • Signatura = název metody + parametry + návratový typ + výjimky
  • Kontrakt = povolené hodnoty parametrů, garantovaná chování

🛡️ Další principy správné konstrukce

Ošetření chyb:

  • Chránit program před nevalidními vstupy (importy, uživatelské vstupy, API)
  • Způsoby zpracování chyby: vrácení neutrální hodnoty, zalogování varování, vrácení chybového kódu, vyvolání výjimky, ukončení programu
  • Pozor: Vrácení neutrální hodnoty (null, 0) může „zamaskovat chybu" – problém se projeví až jinde a obtížně se debuguje

Logování:

  • Úrovně: DEBUG, INFO, WARN, ERROR, FATAL
  • Logger – třída pro zápis do logu; Appender/Handler – kam se logy zapisují (soubor, konzole, email, databáze)
  • Formátování logů: datum, čas, zpráva, úroveň, třída
  • Existující řešení: Java Logging (JUL), LOG4J, SLF4J (fasáda), NLog (.NET)

Štábní kultura (konvence psaní kódu):

  • Pojmenování tříd, metod a parametrů musí být srozumitelné a samopopisné
  • PascalCase pro třídy, camelCase pro metody/proměnné, SNAKE_CASE pro konstanty
  • Špatný kód: float z = 50+y(x.x());. Dobrý kód: float pokuta = pokutaZaZpozdeni(delkaZpozdeni); float celkem = SPRAVNI_POPLATEK + pokuta;
  • Konfigurace do externích souborů (ne hardcoded konstanty v kódu) – umožňuje přizpůsobení prostředím bez rekompilace
  • Texty GUI do externích souborů – umožňuje lokalizaci

Délka metody: Čím delší metoda, tím vyšší pravděpodobnost chyby. Doporučení: jedna obrazovka (100-200 řádků max). Prázdný řádek v metodě = signál pro extrahování.

Nepodřizovat návrh optimalizaci: Není vhodné optimalizovat předčasně. Je důležitější připravit kód na budoucí změny. Optimalizovat teprve po profileru.

♻️ Refactoring

Refactoring je změna vnitřní struktury kódu bez změny jeho vnějšího chování. Provádí se v malých, bezpečných krocích. Nutnou podmínkou je existence automatizovaných testů, které prokáží, že chování se nezměnilo.

Proč refactoring? Kód se degraduje časem (technický dluh). Refactoring ho udržuje srozumitelným, testovatelným a rozšiřitelným. Investice do refactoringu snižuje budoucí náklady na údržbu.

Příznaky, kdy je vhodné začít s refactoringem (Code Smells):

  • Duplicity – stejný nebo velmi podobný kód na více místech (porušení DRY)
  • Dlouhé metody – metoda dělá příliš mnoho, těžko se čte a testuje
  • Velké třídy – třída má příliš mnoho zodpovědností (nízká soudržnost, porušení SRP)
  • Příliš mnoho parametrů – metoda s více než 3-4 parametry je příznakem špatné abstrakce
  • Komentáře – vysvětlující komentář v kódu je příznakem, že kód není dostatečně samopopisný
  • Složité struktury podmínek – hluboce zanořené if/else nebo long switch
  • Nedodržené jmenné konvence
⚠️ Bez testů refactoring nelze bezpečně provádět!

Testy jsou „záchranná síť" při refactoringu. Bez nich nemůžeme prokázat, že jsme nezměnili chování. Každý krok refactoringu musí být ověřen spuštěním testů.

Vybrané techniky refactoringu:

TechnikaPopisKdy použít
Přejmenování metody Přejmenovat metodu tak, aby název jasně vyjadřoval záměr Název metody není srozumitelný nebo je zavádějící
Extrahování metody Vyčlenit část kódu do nové metody se smysluplným názvem Dlouhá metoda, duplikace, komentář v kódu
Zapouzdření atributu Nahradit přímý přístup k atributu getter/setter metodami Public atribut nebo potřeba validace při přístupu
Nahrazení dědičnosti delegováním Místo dědičnosti použít kompozici + delegování Porušení LSP, nevhodná dědičnost
Nahrazení chybového kódu výjimkou Místo -1 nebo null vrátit výjimku při chybě Chybový kód se ignoruje nebo maskuje problém
Skrytí metody Změnit viditelnost metody z public na private/protected Metoda je pouze interní detail implementace
Zavedení Null objektu Místo vracení null vrátit speciální objekt s neutrálním chováním Kód je plný kontrol if (x != null)
Zavedení objektu jako parametru Nahradit skupinu parametrů jedním objektem Metoda má příliš mnoho parametrů, které spolu logicky souvisí