Muistikuvio - Memento pattern

Memento kuvio on suunnittelumalli , joka tarjoaa mahdollisuuden palauttaa objektin edellisen tilan ( kumoa kautta palautus).

Muistikuvio toteutetaan kolmella esineellä: alullepanija , talonmies ja muistomerkki . Alkuperäinen on jokin objekti, jolla on sisäinen tila. Talonmies aikoo tehdä jotain alullepanijalle, mutta haluaa voida kumota muutoksen. Talonmies kysyy ensin tekijältä muistio -esineen. Sitten se tekee mitä tahansa toimintoa (tai toimintojen järjestystä), jonka se aikoi tehdä. Jos haluat palata ennen toimintoja olevaan tilaan, se palauttaa muistio -objektin lähettäjälle. Muistiobjekti itsessään on läpinäkymätön esine (jota talonmies ei voi tai ei saa muuttaa). Tätä mallia käytettäessä on oltava varovainen, jos lähettäjä voi muuttaa muita objekteja tai resursseja - muistikuvio toimii yhdellä objektilla.

Klassisia esimerkkejä muistikuviosta ovat näennäissatunnaislukugeneraattori (jokainen PRNG: n kuluttaja toimii talonmiehenä, joka voi alustaa PRNG: n (alullepanijan) samalla siemenellä (muistoesine) tuottaakseen saman pseudo -satunnaislukujonon) ja tila on äärellinen tilakone .

Yleiskatsaus

Memento-suunnittelumalli on yksi kaksikymmentäkolmesta tunnetusta GoF-suunnittelumallista, jotka kuvaavat toistuvien suunnitteluongelmien ratkaisemista joustavan ja uudelleenkäytettävän olio-ohjelmiston, eli kohteiden, jotka on helpompi toteuttaa, muuttaa, testata ja uudelleenkäyttö. Memento -kuvion loivat Noah Thompson, David Espiritu ja tohtori Drew Clinkenbeard varhaisille HP -tuotteille.

Mitä ongelmia Memento -suunnittelumalli voi ratkaista?

  • Objektin sisäinen tila on tallennettava ulkoisesti, jotta objekti voidaan palauttaa tähän tilaan myöhemmin.
  • Objektin kotelointia ei saa rikkoa.

Ongelmana on, että hyvin suunniteltu objekti on koteloitu niin, että sen esitys (tietorakenne) on piilotettu objektin sisään eikä sitä voida käyttää objektin ulkopuolelta.

Mitä ratkaisua Memento -suunnittelumalli kuvaa?

Aseta esine (tekijä) itse vastuuseen

  • tallentamalla sisäinen tila (muisto) -objektiin ja
  • palauttaminen edelliseen tilaan (muistio) -objektista.

Vain muistion luonut alullepanija saa käyttää sitä.

Asiakas (talonmies) voi pyytää muistion lähettäjältä (tallentaakseen alkuperäisen tilan) ja välittää muistion takaisin lähettäjälle (palauttaakseen aiempaan tilaan).

Tämä mahdollistaa tallentajan sisäisen tilan tallentamisen ja palauttamisen rikkomatta sen kotelointia.

Katso myös alla oleva UML -luokka- ja sekvenssikaavio.

Rakenne

UML -luokka- ja sekvenssikaavio

Image
Esimerkki UML -luokasta ja sekvenssikaaviosta Memento -suunnittelumallille.

Edellä UML luokkakaavio , Caretakerluokan viittaa Originatorluokan säästö ( createMemento()) ja palauttaa ( restore(memento)) alullepanijan sisäisen tilan. Luokka -välineet (1) luomalla ja palauttamalla esine, joka tallentaa antajan nykyinen sisäinen tila ja (2) palauttamalla tilaan kulunut in objekti.
Originator
createMemento()Memento
restore(memento)Memento

UML kaavio esittää ajonaikaisen vuorovaikutukset:
(1) tallentaminen antajan sisäinen tila: Tällä Caretakeresine puhelut createMemento()on Originatoresine, joka luo Mementoobjektin, tallentaa sen nykyinen sisäisen tilan ( setState()), ja palauttaa Mementosen Caretaker.
(2) palauttaminen antajan sisäinen tila: Tällä Caretakerpuhelut restore(memento)on Originatorkohteen ja määritetään Mementoobjekti, joka tallentaa todetaan, että olisi palautettava. OriginatorSaa valtion ( getState()) pois Mementoasettaa omat tilaan.

Java -esimerkki

Seuraava Java -ohjelma havainnollistaa muistikuvion "kumoamista".

import java.util.List;
import java.util.ArrayList;
class Originator {
    private String state;
    // The class could also contain additional data that is not part of the
    // state saved in the memento..
 
    public void set(String state) {
        this.state = state;
        System.out.println("Originator: Setting state to " + state);
    }
 
    public Memento saveToMemento() {
        System.out.println("Originator: Saving to Memento.");
        return new Memento(this.state);
    }
 
    public void restoreFromMemento(Memento memento) {
        this.state = memento.getSavedState();
        System.out.println("Originator: State after restoring from Memento: " + state);
    }
 
    public static class Memento {
        private final String state;

        public Memento(String stateToSave) {
            state = stateToSave;
        }
 
        // accessible by outer class only
        private String getSavedState() {
            return state;
        }
    }
}
 
class Caretaker {
    public static void main(String[] args) {
        List<Originator.Memento> savedStates = new ArrayList<Originator.Memento>();
 
        Originator originator = new Originator();
        originator.set("State1");
        originator.set("State2");
        savedStates.add(originator.saveToMemento());
        originator.set("State3");
        // We can request multiple mementos, and choose which one to roll back to.
        savedStates.add(originator.saveToMemento());
        originator.set("State4");
 
        originator.restoreFromMemento(savedStates.get(1));   
    }
}

Lähtö on:

Originator: Setting state to State1
Originator: Setting state to State2
Originator: Saving to Memento.
Originator: Setting state to State3
Originator: Saving to Memento.
Originator: Setting state to State4
Originator: State after restoring from Memento: State3

Tässä esimerkissä tilana on merkkijono, joka on muuttumaton Java -objekti . Tosielämän skenaarioissa tila on lähes aina muuttuva esine, jolloin tilasta on tehtävä kopio.

On sanottava, että esitetyllä toteutuksella on haittapuoli: se julistaa sisäisen luokan. Olisi parempi, jos tämä muistostrategia voisi koskea useampaa kuin yhtä alullepanijaa.

On pääsääntöisesti kolme muuta tapaa saavuttaa Memento:

  1. Serialisointi.
  2. Luokka ilmoitettu samassa paketissa.
  3. Objektia voidaan käyttää myös välityspalvelimen kautta, joka voi saavuttaa minkä tahansa objektin tallennus-/palautustoiminnon.

C# esimerkki

Muistikuvion avulla voidaan kaapata objektin sisäinen tila rikkomatta kapselointia siten, että myöhemmin voidaan kumota/palauttaa muutokset tarvittaessa. Tässä voidaan nähdä, että muistio -objektia todella käytetään objektiin tehtyjen muutosten palauttamiseen .

class Memento
{
    private readonly string savedState;

    private Memento(string stateToSave)
    {
        savedState = stateToSave;
    }

    public class Originator
    {
        private string state;
        // The class could also contain additional data that is not part of the
        // state saved in the memento.

        public void Set(string state)
        {
            Console.WriteLine("Originator: Setting state to " + state);
            this.state = state;
        }

        public Memento SaveToMemento()
        {
            Console.WriteLine("Originator: Saving to Memento.");
            return new Memento(state);
        }

        public void RestoreFromMemento(Memento memento)
        {
            state = memento.savedState;
            Console.WriteLine("Originator: State after restoring from Memento: " + state);
        }
    }
}

class Caretaker
{
    static void Main(string[] args)
    {
        List<Memento> savedStates = new List<Memento>();

        Memento.Originator originator = new Memento.Originator();
        originator.Set("State1");
        originator.Set("State2");
        savedStates.Add(originator.SaveToMemento());
        originator.Set("State3");
        // We can request multiple mementos, and choose which one to roll back to.
        savedStates.Add(originator.SaveToMemento());
        originator.Set("State4");

        originator.RestoreFromMemento(savedStates[1]);
    }
}

Esimerkki Pythonista

"""
Memento pattern example.
"""


class Memento:
    def __init__(self, state) -> None:
        self._state = state

    def get_saved_state(self):
        return self._state


class Originator:
    _state = ""

    def set(self, state) -> None:
        print("Originator: Setting state to", state)
        self._state = state

    def save_to_memento(self) -> Memento:
        print("Originator: Saving to Memento.")
        return Memento(self._state)

    def restore_from_memento(self, memento) -> None:
        self._state = memento.get_saved_state()
        print("Originator: State after restoring from Memento:", self._state)


saved_states = []
originator = Originator()
originator.set("State1")
originator.set("State2")
saved_states.append(originator.save_to_memento())

originator.set("State3")
saved_states.append(originator.save_to_memento())

originator.set("State4")

originator.restore_from_memento(saved_states[1])

Viitteet

Ulkoiset linkit