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

Dnes sa pozrieme na ďalší návrhový vzor Flyweight (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).

Čo je návrhový vzor Flyweight?

Flyweight je návrhový vzor, ktorý sa primárne používa na zníženie počtu vytvorených objektov, pretože sa pokúša znova použiť už existujúce objekty s rovnakými alebo podobnými vlastnosťami na úrovni triedy a nový objekt vytvorí až vtedy, keď sa nenájde žiadny zodpovedajúci objekt. Tým sa minimalizuje množstvo využívanej pamäte pre údaje zdieľanými medzi podobnými objektami.

V článku sa dozvieš:

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

Rieši problém v aplikáciách, ktoré pracujú s veľkým počtom objektov, ktoré by pri samostatnom uložení zaberali množstvo pamäte a nezanedbateľné sú aj náklady na ich vytváranie. Tento vzor rieši problém tak, že spoločné dáta sa extrahujú a zdieľajú medzi objektami, čím sa znižuje množstvo potrebnej pamäte a využívajú sa už vytvorené objekty.

Príklad Flyweight implementácie v Jave

Teraz si napíšeme program, ktorý bude simulovať jednoduchý systém na prevádzku pizzerie a na ňom si demonštrujeme práve použitie návrhového vzoru Flyweight na efektívne zdieľanie inštancií pízz a minimalizáciu počtu vytvorených objektov. Program vytvára pizzeriu, prijíma objednávky rôznych druhov pízz a obsluhuje ich. Bude pozostávať z tried Pizza, ObjednavkaPizzeriaMain.

Pizza.java

package designpatterns;

import java.util.HashMap;
import java.util.Map;

public class Pizza {
    private static Map<String, Pizza> pizzaCache = new HashMap<>();
    private String nazov;

    private Pizza(String nazov) {
        this.nazov = nazov;
    }

    public static Pizza intern(String nazov) {
        pizzaCache.putIfAbsent(nazov, new Pizza(nazov));
        return pizzaCache.get(nazov);
    }

    public static int pocetDruhov() {
        return pizzaCache.size();
    }

    @Override
    public String toString() {
        return nazov;
    }
}

Trieda Pizza reprezentuje druh pizzy a využíva vzor Flyweight na zdieľanie inštancií pízz s rovnakým názvom. Má metódu intern(), ktorá vytvára novú pizzu len v prípade, ak ešte neexistuje, a metódu pocetDruhov(), ktorá vracia počet rôznych druhov pízz.

Objednavka.java

package designpatterns;

public class Objednavka {
    private Pizza pizza;
    private int cisloStolu;

    private Objednavka(Pizza pizza, int cisloStolu) {
        this.pizza = pizza;
        this.cisloStolu = cisloStolu;
    }

    public static Objednavka create(String nazov, int cisloStolu) {
        Pizza pizza = Pizza.intern(nazov);
        return new Objednavka(pizza, cisloStolu);
    }

    @Override
    public String toString() {
        return "Posielam pizzu " + pizza + " na stol " + cisloStolu;
    }
}

Trieda Objednavka reprezentuje objednávku pizzy. Každá objednávka obsahuje odkaz na konkrétnu pizzu a číslo stolu, kam má byť pizza doručená.

Pizzeria.java

package designpatterns;

public class Pizzeria {
    private java.util.List<Objednavka> objednavky = new java.util.ArrayList<>();

    public void prijatObjednavku(String druh, int cisloStolu) {
        objednavky.add(Objednavka.create(druh, cisloStolu));
    }

    public void obsluzit() {
        for (Objednavka order : objednavky) {
            System.out.println(order);
        }
    }

    public int pocetPrijatychObjednavok() {
        return objednavky.size();
    }
}

Trieda Pizzeria predstavuje pizzeriu, ktorá prijíma objednávky a poskytuje metódy na spracovanie a výpis objednávok. Taktiež poskytuje metódu na získanie počtu prijatých objednávok.

Main.java

import designpatterns.Pizzeria;
import designpatterns.Pizza;

public class Main {
    public static void main(String[] args) {
        Pizzeria pizzeria = new Pizzeria();
        pizzeria.prijatObjednavku("Hawai", 2);
        pizzeria.prijatObjednavku("Quattro Formaggi", 1);
        pizzeria.prijatObjednavku("Margherita", 1);
        pizzeria.prijatObjednavku("Napoletana", 5);
        pizzeria.prijatObjednavku("Hawai", 4);
        pizzeria.prijatObjednavku("Quattro Formaggi", 3);
        pizzeria.prijatObjednavku("Margherita", 3);
        pizzeria.prijatObjednavku("Hawai", 3);
        pizzeria.prijatObjednavku("Margherita", 6);
        pizzeria.prijatObjednavku("Quattro Formaggi", 2);
        pizzeria.obsluzit();
        System.out.println();
        System.out.println("Pocet prijatych objednavok: " + pizzeria.pocetPrijatychObjednavok());
        System.out.println("Pocet pouzitych objektov typu pizza: " + Pizza.pocetDruhov());
    }
}

V metóde main je vytvorená inštancia pizzerie a potom sú vytvorené rôzne objednávky na pizze. Všetky objednávky sú obslúžené a nakoniec sa vypíše počet prijatých objednávok a počet použitých objektov typu Pizza. Tým sme preukázali zdieľanie objektov typu Pizza medzi rozdielnymi objednávkami.

Výstup z tohto príkladu je:

Screenshot výstupu z príkladu

Zhrnutie

Návrhový vzor Flyweight sa používa v situácii, keď potrebujeme ukladať do vyrovnávacej pamäte a opätovne využívať inštancie objektov, ktoré sú si podobné. Je zameraný najmä na optimalizovanie réžie vytvárania objektov a ich nárokov na pamäť.

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 Flyweight tu.

Ak si Java programátor a hľadáš prácu, pozri si naše firemné benefity a reaguj na najnovšie pracovné ponuky.

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ť