# 5 • Návrhové vzory (DP)

*Singleton, Observer, Factory Method, Command*

## Definice

- opakovaně použitelná řešení často se opakujících problémů v návrhu OOP softwaru
- nejsou to hotové knihovny nebo kus kódu - jsou to způsoby, jak strukturovat třídy a jejich vztahy
- **původ:** kniha *Design Patterns: Elements of Reusable Object-Oriented Software* (1994) od čtveřice autorů známé jako **Gang of Four (GoF)**
    - Gamma, Helm, Johnson, Vlissides
    - definovali 23 vzorů rozdělených do tří kategorií

## **Tři kategorie GoF**

- vytvářecí (creational)
    - řeší, **jak vytvářet objekty**, aby kód nebyl pevně svázaný s konkrétními třídami
    - **Singleton**, **Factory Method,** Builder, Abstract Factory, Prototype…
- strukturální (structural)
    - řeší, jak skládat třídy/objekty do větších celků
    - Adapter, Composite, Facade…
- behaviorální (behavioral)
    - řeší **komunikaci mezi objekty** a rozdělení odpovědnosti
    - **Observer**, **Command**, Strategy, State…

---

## **Singleton (vytvářecí)**

**Účel:** zaručí, že daná třída má **právě jednu instanci** v celé aplikaci, a poskytne k ní globální přístup.

**Princip:**

- privátní konstruktor (nikdo zvenku nemůže udělat `new`)
- statická metoda `getInstance()`, která instanci vrátí (a při prvním volání vytvoří)
- statické pole drží referenci

```csharp
class Logger {
    private static Logger instance;
    private Logger() {}                  // nikdo zvenku nemůže new
    public static Logger getInstance() {
        if (instance == null)
            instance = new Logger();     // lazy initialization
        return instance;
    }
}
```

**Použití:** logger, konfigurace, cache, Unity: třídy typu `GameManager`, `AudioManager`.

**Výhody:**

- jistota, že existuje jen jedna instance (např. jeden zápis do log souboru)
- globální přístup z celé aplikace
- lazy initialization - instance se vytvoří až když je potřeba

**Nevýhody:**

- v podstatě **globální proměnná** → skryté závislosti mezi částmi kódu
- porušuje **Single Responsibility Principle** (třída řeší jak svou logiku, tak svůj životní cyklus)
- špatně se **testuje** (nejde mockovat, drží stav mezi testy)
- problémy ve **vícevláknovém prostředí** (dvě vlákna mohou současně projít `if == null` a vytvořit dvě instance — řeší se `synchronized`, `double-checked locking`, nebo enum implementací)
- často považován za **anti-pattern**, pokud se používá místo dependency injection

---

## Observer (behaviorální)

**Účel:** definuje vztah **1:N** mezi objekty – když jeden objekt (Publisher) změní stav, všichni jeho odběratelé (Subscribers / Observers) jsou automaticky informováni.

[23 • Událostmi řízené programování](23%20%E2%80%A2%20Ud%C3%A1lostmi%20%C5%99%C3%ADzen%C3%A9%20programov%C3%A1n%C3%AD%202b592fa18d8f802f88dfd5b330c0c8ac.md)

- základ událostmi řízeného programování
- Publisher posílá událost přihlášeným Subscriberům

**Princip:**

- Publisher drží seznam observerů a metody `subscribe()`, `unsubscribe()`, `notify()`
- Subscriber implementuje rozhraní s metodou `update()`, kterou Subject volá
- Subscriber se sám přihlásí, Publisher ho jen obslouží - **nezná konkrétní typy** odběratelů

**Použití:**

- GUI eventy (kliknutí na tlačítko → handler)
- C# `event` / `delegate`, JavaScript `addEventListener`
- reaktivní knihovny (RxJS)
- MVC - View pozoruje Model
- pub/sub messaging (Apache Kafka, MQTT)

výhody:

- **loose coupling** - Subject a Observer se znají jen přes rozhraní
- dynamické přihlašování/odhlašování za běhu
- jeden Subject může mít libovolný počet odběratelů různých typů

nevýhody:

- **memory leaks**, pokud se observer zapomene odhlásit (Subject ho drží v seznamu, GC ho neuvolní)
- **neřízené pořadí** notifikací - nemůžeš spoléhat, kdo dostane update první
- těžké **debugovat** - když přijde event, není okamžitě jasné, kdo všechno na něj zareaguje (kaskáda updatů)
- riziko nekonečné smyčky (Observer při updatu změní Subject → další notifikace → …)

---

## Factory Method (vytvářecí)

**Účel:** umožňuje třídě delegovat vytváření instancí na své podtřídy. Klient pracuje s abstraktním rozhraním, neví, jaký konkrétní typ dostane.

**Princip:**

- abstraktní třída / interface s metodou `createProduct()` (= Factory Method)
- každá podtřída ji přepíše a vrátí jiný konkrétní typ
- zbytek kódu v rodičovské třídě používá `createProduct()` aniž by věděl, co přesně vznikne

```java
abstract class Dialog {
    void render() {
        Button b = createButton();   // Factory Method
        b.render();
    }
    abstract Button createButton();  // podtřídy rozhodnou
}

class WindowsDialog extends Dialog {
    Button createButton() { return new WindowsButton(); }
}
class MacDialog extends Dialog {
    Button createButton() { return new MacButton(); }
}
```

Použití:

- cross-platform UI (jeden kód, různé widgety podle OS)
- různé typy dokumentů v textovém editoru
- parsery (XML/JSON/YAML) vybírané podle vstupu
- ORM - `EntityManager.createQuery()`

Výhody:

- **Open/Closed Principle** - přidání nového typu znamená přidat podtřídu, ne měnit existující kód
- odděluje **vytváření** objektu od jeho **použití**
- klient nezávisí na konkrétních třídách, jen na rozhraní

**Nevýhody:**

- **víc tříd** = vyšší složitost u jednoduchých případů
- vyžaduje hierarchii dědičnosti (Factory Method = polymorfismus)
- pro lidi nečtoucí GoF může být zbytečně abstraktní

---

## **Command (behaviorální)**

**Účel:** zabalí **požadavek (akci) jako objekt**. Místo přímého volání metody vznikne objekt, který nese informaci *co* udělat a *na čem* - dá se uložit do fronty, logovat, vrátit zpět (undo), zopakovat.

**Princip - typické role:**

- **Command** - rozhraní s metodou `execute()` (často i `undo()`)
- **ConcreteCommand** - implementace konkrétní akce, drží odkaz na receivera a parametry
- **Receiver** - objekt, který akci skutečně provede
- **Invoker** - spouštěč (např. tlačítko), neví nic o tom, co Command dělá
- **Client** - vytváří Command a předává ho Invokerovi

```java
interface Command { void execute(); void undo(); }

class CopyCommand implements Command {
    Editor editor;
    String backup;
    CopyCommand(Editor e) { editor = e; }
    void execute() { backup = editor.getSelection(); 
                     editor.copyToClipboard(); }
    void undo()    { /* clipboard zpět */ }
}

class Button {                    // Invoker
    Command command;
    void click() { command.execute(); }
}
```

**Použití:**

- **Undo/Redo** (Ctrl+Z) (Photoshop, IDE, Word): historie je stack commandů
- **makra** - sekvence commandů, kterou lze přehrát
- **GUI tlačítka a menu** - stejná akce dosažitelná z víc míst (toolbar, klávesa, menu)
- **queueing / scheduling** - joby ve frontě, transakce v DB
- **logování operací** (audit log, event sourcing)
- network requesty (zabalit požadavek, poslat po síti, provést na druhé straně)

**Výhody:**

- **odděluje** odesílatele akce (Invoker) od příjemce (Receiver) - tlačítko neví, co se po stisku stane
- snadno se realizuje **undo/redo** (každý Command si pamatuje, jak se vrátit)
- commandy lze **kombinovat** (makro = složený command), **frontovat**, **logovat**, **odložit**
- Open/Closed - nová akce = nová třída, žádné zásahy do existujícího kódu

**Nevýhody:**

- **hodně malých tříd** - pro každou akci vlastní třída
- pro jednoduché případy overengineering (proč Command, když stačí volat metodu?)
- undo bývá komplikovaný u akcí, které mají vedlejší efekty (síťové volání, soubor smazaný z disku)

---

#### Výhody návrhových vzorů obecně

- **ověřená řešení** - neřešíš problém od nuly, používáš to, co fungovalo tisíckrát
- **společný slovník** - když řekneš "udělej to jako Observer", ostatní vývojáři okamžitě vědí, co tím myslíš
- zlepšují **udržovatelnost** a **rozšiřitelnost** kódu
- podporují principy **SOLID** (zejména Open/Closed a Dependency Inversion)
- usnadňují **komunikaci v týmu** a **code review**

### Nevýhody návrhových vzorů obecně

- **zbytečná složitost** (overengineering) - vzor přidaný "protože je elegantní" zhorší kód
- riziko **nadužívání** - někdy stačí obyčejná funkce nebo `if`
- **vyšší vstupní bariéra** pro nezkušené vývojáře (víc tříd, víc abstrakce, hůř se čte)
- mohou **překrývat jednoduchá řešení** - místo přímočarého kódu se objeví hierarchie pěti tříd
- vzory z roku 1994 jsou částečně **kompenzací slabin starších jazyků** - v moderním Pythonu, JavaScriptu nebo Kotlinu se některé řeší vestavěnými prostředky (lambdy, first-class funkce) bez celé struktury tříd
- **Singleton** je v komunitě často považován přímo za anti-pattern

---

### Rychlý tahák - jak poznat vzor

| Vzor | Klíčové znaky na diagramu |
| --- | --- |
| **Singleton** | privátní konstruktor + statická `Instance` property |
| **Factory Method** | abstraktní `Creator` + abstraktní `FactoryMethod()` + podtřídy |
| **Observer** | Subject s `Attach/Detach/Notify` + Observer s `Update()` |
| **Command** | rozhraní `ICommand` + `Execute()` + Invoker + Receiver odděleně |

---

### Ostatní vzory (stručně)

| Vzor | Skupina | Jedna věta |
| --- | --- | --- |
| **Pool** | Vytvářecí | Znovupoužívá existující instance místo vytváření nových (DB připojení) |
| **Flyweight** | Strukturální | Sdílí společná data mezi mnoha objekty, šetří paměť |
| **Immutable** | Strukturální | Objekt nelze po vytvoření změnit, změna = nový objekt (string v C#) |
| **Template Method** | Behaviorální | Rodič definuje kostru algoritmu, potomci doplní konkrétní kroky |
| **Servant** | Behaviorální | Sdílená funkčnost pro třídy bez společného předka |