Návrhové vzory Java (design patterns): Composite

Dnes sa pozrieme na ďalší návrhový vzor Composite (java design patterns) z kategórie štrukturálnych vzorov (structural patterns). Návrhové vzory v tejto kategórii sa zaoberajú štruktúrou triedy, ako je dedičnosť (inheritance) a kompozícia (composition).

Prečítaj si o ďalších design patterns – seriál návrhových vzorov:

Čo to je návrhový vzor Composite?

Design pattern Composite (kompozit) je návrhový vzor, ktorý sa používa na vytvorenie stromovej štruktúry objektov, ktorá umožňuje klientovi pracovať s jednotlivými objektmi a ich zoskupeniami (kompozitmi) rovnakým spôsobom, pretože majú rovnaké rozhranie.

Aký problém návrhový vzor Composite rieši?

Pri práci so stromovou štruktúrou musia programátori často rozlišovať medzi listovým uzlom a vetvou. Vďaka tomu je kód zložitejší, a preto je náchylnejší na chyby. Riešením je rozhranie, ktoré umožňuje jednotne zaobchádzať so zložitými a primitívnymi objektmi.

Problém, ktorý Composite rieši, je potreba pracovať s objektami a ich kompozitmi jednotným spôsobom. Tým umožňuje klientovi rekurzívne prechádzať a manipulovať s celou stromovou štruktúrou, pričom nepotrebujeme rozlišovať medzi jednotlivými objektami a ich zoskupeniami.

Príklad Composite implementácie v Jave

Teraz si ukážeme ako implementovať vzor Composite v Jave. Vytvoríme si komplexnú stromovú štruktúru zloženú z individuálnych objektov (listov) aj kompozícií objektov (vetiev). Použitím vzoru Composite dosiahneme jednotné vykonávanie operácií na jednotlivých častí stromu nezávisle od typu.

Rozhranie CastStromu reprezentuje spoločné rozhranie pre listové uzly (List) aj zložené uzly (Vetva). Toto rozhranie definuje metódu vykonajOperaciu(), ktorá je kľúčová pre tento vzor.

CastStromu.java

package designpatterns;

// CastStromu - rozhranie pre všetky objekty v strome
public interface CastStromu {
    void vykonajOperaciu();
}

Trieda List reprezentuje listový komponent v hierarchii. Implementuje rozhranie CastStromu a poskytuje implementáciu metódy vykonajOperaciu(). Listové komponenty nemajú žiadne podriadené prvky a vykonávajú operácie.

List.java

package designpatterns;

// List - konkrétna časť stromu reprezentujúca list v strome
public class List implements CastStromu {
    private String id;

    public List(String id) {
        this.id = id;
    }

    public void vykonajOperaciu() {
        System.out.println("Vykonávam operáciu na liste s názvom: " + id);
    }
}

Trieda Vetva reprezentuje zložený komponent v hierarchii. Tiež implementuje rozhranie CastStromu a udržuje kolekciu (castiStromu) podriadených komponentov, ktoré môžu byť buď listové komponenty, alebo iné zložené komponenty.

Metóda pridajCast() umožňuje pridávať podriadené komponenty a tým konštruovať hierarchickú štruktúru.

Vetva.java

package designpatterns;

import java.util.ArrayList;

// Konkrétna časť stromu reprezentujúca vetvu v strome
public class Vetva implements CastStromu {
    private String id;
    private java.util.List<CastStromu> castiStromu = new ArrayList<>();

    public Vetva(String id) {
        this.id = id;
    }

    public void pridajCast(CastStromu cast) {
        castiStromu.add(cast);
    }

    public void vykonajOperaciu() {
        System.out.println("Vykonávam operáciu na vetve s názvom: " + id);
        for (CastStromu cast : castiStromu) {
            cast.vykonajOperaciu();
        }
    }
}

V metóde main je vytvorená inštancia koreňového zloženého komponentu (kmenovaVetva) a k nemu sú pridané podriadené komponenty (ako listové, tak zložené).

Keď je zavolaná metóda vykonajOperaciu() na kmeňovom komponente (kmenovaVetva), rekurzívne zavolá metódu vykonajOperaciu() na všetkých podriadených komponentoch, čo vedie k vykonaniu operácií na celej štruktúre.

Main.java

import designpatterns.*;

public class Main {
    public static void main(String[] args) {
        // Vytvorí strom z listov a vetiev
        System.out.println("Skladám strom z kmeňa, listov a vetiev.");
        List list1 = new List("List 1");
        List list2 = new List("List 2");
        List list3 = new List("List 3");

        Vetva vetva1 = new Vetva("Vetva 1");
        vetva1.pridajCast(list1);
        vetva1.pridajCast(list2);

        Vetva vetva2 = new Vetva("Vetva 2");
        vetva2.pridajCast(list3);

        Vetva kmenovaVetva = new Vetva("Kmeň");
        kmenovaVetva.pridajCast(vetva1);
        kmenovaVetva.pridajCast(vetva2);

        // Vykonáva operáciu na celej stromovej štruktúre
        kmenovaVetva.vykonajOperaciu();
    }
}

Výstup z tohto príkladu je:

výstup kódu v Java z príkladu Composite
výstup kódu

Zhrnutie Java Composite

Návrhový vzor Composite sa používa v situácii, keď je potrebné pracovať jednotne s jednotlivými objektami aj ich zoskupeniami.

Pripravili sme pre teba súbory so spomínaným príkladom vo forme kódu, ktorý si môžeš spustiť priamo v Jave. Stiahni si kód Java Composite tu.

O autorovi

Jozef Wagner

Java Developer Senior

Viac ako 10 rokov programujem v Jave, momentálne pracujem v msg life Slovakia ako Java programátor senior a pomáham zákazníkom implementovať ich požiadavky do poistného softvéru Life Factory. Vo voľnom čase si rád oddýchnem v lese, prípadne si zahrám nejakú dobrú počítačovú hru.

Daj nám o sebe vedieť