Decorator


V této části se podíváme na návrhový vzor Decorator. Můžeme jej použít, když chceme nějakou třídu rozšířit o novou funkcionalitu ale nechceme ji měnit.

Proč Decorator použít

Pokud chceme nějakou třídu rozšířit o nějakou další funkcionalitu, tak můžeme použít dědičnost, pokud je to možné. Jednoduše vytvoříme novou třídu dědící od třídy, které chceme přidat novou funkcionalitu. Další možností je použít návrhový vzor Decorator. Ten je o tom, že se namísto vytváření třídy dědící od třídy, které chceme přidat novou funkcionalitu, vytváří třída, která pracuje s instancí této třídy. Tímto způsobem tedy přidává novou funkcionalitu bez použití dědičnosti.

Decorator je často užitečný pro dodržování principu jedné odpovědnosti, protože umožňuje rozdělit třídy na části, které mají jednu odpovědnost.

Příklad - Přidání nové funkcionality

Následující ukázka ukazuje třídy Kruh a Ctverec, které představují geometrické tvary. Třída Kruh obsahuje jen vlastnost určující poloměr kruhu a třída Ctverec obsahuje jen vlastnost určující stranu čtverce. Pro ukázku přidání nějaké další funkcionality, jsou v ukázce ukázány třídy BarevnyTvar a PruhlednyTvar. Třída BarevnyTvar je Decorator umožňující vytvořit barevný tvar a třída PruhlednyTvar je Decorator, který umožňuje vytvořit průhledný tvar.

class Tvar {
    constructor() {
        if (this.constructor.name === "Tvar")
            throw new Error("Tvar je abstraktní!");
    }
}

class Kruh extends Tvar {
    constructor(polomer=0) {
        super();
        this.polomer = polomer;
    }

    toString() {
        return `Kruh o poloměru ${this.polomer}`;
    }
}

class Ctverec extends Tvar {
    constructor(strana=0) {
        super();
        this.strana = strana;
    }

    toString() {
        return `Čtverec o straně ${this.strana}`;
    }
}


// Decorator pro vytvoření barevného tvaru
class BarevnyTvar extends Tvar {
    constructor(tvar, barva) {
        super();
        this.tvar = tvar; // dekorovaný objekt
        this.barva = barva; // nová vlastnost dekorovaného objektu
    }

    toString() {
        return `${this.tvar.toString()} má barvu ${this.barva}`;
    }
}

// Decorator pro vytvoření průhledného tvaru
class PruhlednyTvar extends Tvar {
    constructor(tvar, pruhlednost) {
        super();
        this.tvar = tvar; // dekorovaný objekt
        this.pruhlednost = pruhlednost; // nová vlastnost dekorovaného objektu
    }

    toString() {
        return `${this.tvar.toString()} má ${this.pruhlednost * 100}% průhlednost`;
    }
}


// vytvoření kruhu
const kruh = new Kruh(4);
console.log(kruh.toString());

// vytvoření barevného kruhu (třída BarevnyTvar ale pořád pracuje s objektem kruh)
const zelenyKruh = new BarevnyTvar(kruh, "zelená");
console.log(zelenyKruh.toString());

// vytvoření barevného průhledného kruhu
// - naše Decoratory dědí od třídy Tvar a jako parametr v konstruktoru berou také instanci třídy Tvar
// - můžeme tedy Decorator použít jako tvar pro jiný Decorator
const zelenyPruhlednyKruh = new PruhlednyTvar(zelenyKruh, 0.5);
console.log(zelenyPruhlednyKruh.toString());