Strategy


V této části se podíváme na návrhový vzor Strategy. Tento návrhový vzor rozděluje algoritmy na vyšší a nižší části.

Proč Strategy použít

Návrhový vzor Strategy se týká toho, že některé algoritmy můžeme rozdělit na vyšší a nižší části. Díky tomu můžeme oddělit funkčnost (vyšší část) od implementace (nižší část) a použít tak algoritmus na více věcí.

Jako příklad rozdělení algoritmu na vyšší a nižší část si můžeme ukázat třeba přípravu čaje. Můžeme ji rozdělit na tuto vyšší a nižší část:

  • vyšší část - uvaříme vodu a nalijeme ji do sklenice (tento proces můžeme použít pro přípravu různých horkých nápojů, ne jen čaje)
  • nižší část - vložíme čajový sáček do sklenice (toto je specifický krok pro přípravu čaje)

Příklad - Tvorba listu v různých formátech

Následující ukázka ukazuje třídu TextProcessor, která slouží k tvorbě listu v různých formátech. Můžeme si vybrat ze dvou formátů: markdown a html. Pro oba tyto formáty jsou v ukázce vytvořeny Strategy třídy, které se starají o nižší část algoritmu pro vytváření listu.

const OutputFormat = Object.freeze({
    markdown: 0,
    html: 1
});

// základní třída pro List Strategy třídy
class ListStrategy {
    start(buffer) {}
    konec(buffer) {}
    pridejPolozku(buffer, polozka) {}
}

// Strategy pro Markdown List
class MarkdownListStrategy extends ListStrategy {
    // ne všechny metody musíme přepsat, pokud je
    // nepotřebujeme, tak se budou volat prázdné metody

    pridejPolozku(buffer, polozka) {
        buffer.push(` * ${polozka}`);
    }
}

// Strategy pro HTML List
class HtmlListStrategy extends ListStrategy {
    start(buffer) {
        buffer.push('<ul>');
    }

    konec(buffer) {
        buffer.push('</ul>');
    }

    pridejPolozku(buffer, polozka) {
        buffer.push(`  <li>${polozka}</li>`);
    }
}

class TextProcessor {
    constructor(outputFormat) {
        this.buffer = [];
        this.nastavOutputFormat(outputFormat);
    }

    // pomocí této metody můžeme změnit Strategy
    nastavOutputFormat(format) {
        switch (format) {
            case OutputFormat.markdown:
                this.listStrategy = new MarkdownListStrategy();
                break;
            case OutputFormat.html:
                this.listStrategy = new HtmlListStrategy();
                break;
        }
    }

    // metoda pro připojení předaného listu položek
    pripojList(polozky) {
        // v algoritmu se používá nastavená Strategy
        this.listStrategy.start(this.buffer);
        for (let polozka of polozky)
            this.listStrategy.pridejPolozku(this.buffer, polozka);
        this.listStrategy.konec(this.buffer);
    }

    smaz() {
        this.buffer = [];
    }

    toString() {
        return this.buffer.join('\n');
    }
}


const tp = new TextProcessor(OutputFormat.markdown);
tp.pripojList(['creational', 'structural', 'behavioral']);
console.log(tp.toString());
// vypíše:
// * creational
// * structural
// * behavioral

tp.smaz();

// nastavení jiné Strategy
tp.nastavOutputFormat(OutputFormat.html);
tp.pripojList(['Návrhové', 'Vzory', 'V', 'JS']);
console.log(tp.toString());
// vypíše:
// <ul>
//   <li>Návrhové</li>
//   <li>Vzory</li>
//   <li>V</li>
//   <li>JS</li>
// </ul>