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

Dnes sa pozrieme na ďalší návrhový vzor Memento (java design patterns) z kategórie vzorov správania (behavioral patterns). Návrhové vzory v tejto kategórii sa zaoberajú komunikáciou (interaction) medzi objektami a ich zodpovednosťou (responsibility).

Čo je návrhový vzor Memento?

Návrhový vzor Memento umožňuje uchovať vnútorný stav objektu bez porušenia princípu zapuzdrenia (encapsulation), ktorý je možné v prípade potreby obnoviť a tak vrátiť objekt do svojho pôvodného stavu.

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

V aplikáciách je pomerne časté ukladanie stavu, ktorý používateľ zadal ako vstup do programu, napr. pri vypĺňaní webového formulára. V prípade vypadnutia internetového spojenia je vhodné obnoviť posledný stav zadaných údajov, bez toho aby sme ich od používateľa pýtali ešte raz.

V podobných situáciách, keď potrebujeme uchovávať priebežný stav a ľahko sa k nemu hocikedy vrátiť sa používa návrhový vzor Memento, ktorého princíp spočíva v oddelení ukladania stavu objektu od samotného objektu. Objekt, ktorý má ukladať svoj stav, vytvorí objekt Memento, ktorý obsahuje potrebné informácie o stave. Tento objekt Memento sa potom môže uložiť (napríklad do histórie) a neskôr použiť na obnovenie pôvodného stavu objektu.

Dobre navrhnutý objekt je zapuzdrený, to znamená, že dáta objektu sú skryté vo vnútri a nie sú prístupné zvonku objektu, preto za uloženie vnútorného stavu do (Memento) objektu a jeho obnovenie z (Memento) objektu je zodpovedný samotný objekt – Originator. Klient (Caretaker) si v prípade uloženia stavu preberie Memento objekt od Originator objektu a pri obnovení mu ho odovzdá späť. Tento spôsob umožňuje uložiť a obnoviť vnútorný stav Originator objektu bez porušenia jeho zapuzdrenia.

Príklad Memento implementácie v Jave

Ako používať návrhový vzor Memento, ukladať a obnovovať stav objektu, bez toho aby sa porušil princíp zapuzdrenia si demonštrujeme na jednoduchej hre, kde hráč prechádza postupne nebezpečnými levelmi, kde na každom kroku môže utrpieť zranenie, pričom si môže hocikedy uložiť stav hry a podľa potreby ho obnoviť.

Hra.java

package designpatterns;

public class Hra {
    private int levelId;
    private int zdravie;

    public Hra() {
        spustitHru();
    }

    public void spustitHru() {
        this.levelId = 1;
        this.zdravie = 100;
        System.out.println("Spúšťam novú hru.");
        ukazStavHry();
    }

    public Memento ulozitHru() {
        System.out.println("Ukladám stav hry.");
        return new Memento(levelId, zdravie);
    }

    public void nacitatHru(Memento memento) {
        System.out.println("Načítavam uloženú pozíciu v hre.");
        this.levelId = memento.getLevelId();
        this.zdravie = memento.getZdravie();
    }

    public void dokoncitLevel() {
        levelId++;
        System.out.println("Pokračujem do levelu " + levelId);
        ukazStavHry();
    }

    public void zranitSa(int poskodenie) {
        this.zdravie -= poskodenie;
        System.out.println("Hráč utrpel zranenie (" + -poskodenie + ")");
        ukazStavHry();
        if (zdravie <= 0) {
            System.out.println("Hráč stratil celé zdravie. Hra začne od začiatku.");
            spustitHru();
        }
    }

    public void ukazStavHry() {
        System.out.println("    Level " + levelId + ", Zdravie: " + zdravie);
    }
}

Trieda Hra obsahuje metódy na spustenie hry, ukladanie hry, načítanie hry, dokončenie levelu, zranenie hráča a ukazovanie stavu hry.

Memento.java

package designpatterns;

public class Memento {
    private int levelId;
    private int zdravie;

    public Memento(int levelId, int zdravie) {
        this.levelId = levelId;
        this.zdravie = zdravie;
    }

    public int getLevelId() {
        return levelId;
    }

    public int getZdravie() {
        return zdravie;
    }
}

Trieda Memento slúži ako objekt, ktorý uchováva stav hry. Memento obsahuje informácie o leveli (levelId) a zdraví (zdravie).

Hranie.java

import designpatterns.Hra;
import designpatterns.Memento;

public class Hranie {
    public static void main(String[] args) {
        Hra hra = new Hra();
        hra.zranitSa(12);
        hra.dokoncitLevel();
        Memento ulozenaPozicia = hra.ulozitHru();
        hra.zranitSa(24);
        hra.zranitSa(18);
        hra.dokoncitLevel();
        hra.zranitSa(20);
        hra.zranitSa(12);
        hra.zranitSa(14);
        hra.nacitatHru(ulozenaPozicia);
        hra.ukazStavHry();
    }
}

Trieda Hranie obsahuje main metódu, v ktorej je demonštrované použitie návrhového vzoru Memento. Hráč spustí hru, zraní sa, dokončí level, uloží stav hry (Memento), utrpí ďalšie zranenia, dokončí ďalší level, utrpí ďalšie zranenia a aby nemusel hrať od začiatku, nakoniec si obnoví hru z uloženej pozície.

Výstup z tohto príkladu je:

Výstup príkladu z návrhového vzoru Memento.

Zhrnutie

Vzor Memento je návrhový vzor, ktorý rieši problém uchovávania a obnovovania stavu objektu tak, aby bol schopný vrátiť sa do predchádzajúceho stavu. Často sa používa na implementáciu undo/rollback mechanizmu alebo na uchovávanie histórie stavov objektu.

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

Ak si Java programátor a hľadáš prácu, pozri si naše benefity pre zamestnancov a reaguj na najnovšie ponuky práce.

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ť