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.

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ť