V této části se podíváme na návrhový vzor Memento. Tento návrhový vzor je o tom, že si například můžeme uložit stav nějakého objektu a díky tomu jej v budoucnu obnovit.
Objekt nebo systém může postupem času procházet různými změnami. Pokud chceme tyto změny ukládat, tak máme více možností. Jednou z nich je použít návrhový vzor Command. Další může být použít návrhový vzor Memento, o kterém je tato část.
Narozdíl od návrhového vzoru Command, kde si ukládáme provedené operace, si při použití Mementa ukládáme aktuální stav systému nebo objektu.
Následující ukázka ukazuje třídu BankovniUcet, která reprezentuje bankovní účet. Pomocí metod vlozit a vybrat můžeme s bankovním účtem manipulovat. Tyto metody vracejí memento uchovávající stav účtu po provedení jejich operace, které si můžeme uschovat a později jej použít k obnovení účtu.
// Memento pro třídu BankovniUcet
class Memento {
constructor(zustatek) {
this.zustatek = zustatek;
}
}
class BankovniUcet {
constructor(zustatek = 0) {
this.zustatek = zustatek;
}
vlozit(castka) {
this.zustatek += castka;
// po provedení operace se vrátí nové memento
return new Memento(this.zustatek);
}
vybrat(castka) {
this.zustatek -= castka;
// po provedení operace se vrátí nové memento
return new Memento(this.zustatek);
}
obnovit(memento) {
this.zustatek = memento.zustatek;
}
toString() {
return `Zůstatek: ${this.zustatek}`;
}
}
const ucet = new BankovniUcet(150);
console.log(ucet.toString());
// když provedeme s objektem ucet nějakou operaci, tak se nám vrátí
// memento, pomocí kterého můžeme objekt v budoucnu obnovit
const m1 = ucet.vybrat(50);
const m2 = ucet.vlozit(25);
console.log(ucet.toString());
// obnovení stavu objektu ucet pomocí mementa m1
ucet.obnovit(m1);
console.log(ucet.toString());
Memento můžeme použít k implementaci undo/redo funkcionality. Následující ukázka přidává třídě BankovniUcet z minulého příkladu metody undo a redo. Pomocí těchto metod se můžeme vracet o operaci zpět nebo dopředu.
class Memento {
constructor(zustatek) {
this.zustatek = zustatek;
}
}
class BankovniUcet {
constructor(zustatek = 0) {
this.zustatek = zustatek;
// v tomto poli se ukládají vytvořená mementa
this.zmeny = [new Memento(zustatek)];
// tato vlastnost udává index mementa v poli zmeny,
// které odpovídá aktuálnímu stavu bankovnímu účtu
this.aktualni = 0;
}
vlozit(castka) {
this.zustatek += castka;
// vytvoří se nové memento a přidá se do pole zmeny
let m = new Memento(this.zustatek);
this.zmeny.push(m);
this.aktualni++;
// memento se vrací, pokud bychom si jej chtěli někde uložit
return m;
}
vybrat(castka) {
this.zustatek -= castka;
// vytvoří se nové memento a přidá se do pole zmeny
let m = new Memento(this.zustatek);
this.zmeny.push(m);
this.aktualni++;
// memento se vrací, pokud bychom si jej chtěli někde uložit
return m;
}
// metoda pro obnovení účtu podle předaného mementa
obnovit(memento) {
if (memento) {
this.zustatek = memento.zustatek;
this.zmeny.push(memento);
this.aktualni = this.zmeny.length-1;
}
}
// pomocí této metody se můžeme vrátit o operaci zpět
undo() {
if (this.aktualni > 0) {
let m = this.zmeny[--this.aktualni];
this.zustatek = m.zustatek;
return m;
}
return null;
}
// pomocí této metody se můžeme posunout o operaci dopředu
redo() {
if (this.aktualni+1 < this.zmeny.length) {
let m = this.zmeny[++this.aktualni];
this.zustatek = m.zustatek;
return m;
}
return null;
}
toString() {
return `Zůstatek: ${this.zustatek}`;
}
}
const ucet = new BankovniUcet(100);
ucet.vlozit(50);
ucet.vybrat(25);
console.log(ucet.toString());
// pomocí metod undo a redo se můžeme
// vracet o změnu zpět a vpřed
ucet.undo();
console.log("Undo 1: " + ucet.toString());
ucet.undo();
console.log("Undo 2: " + ucet.toString());
ucet.redo();
console.log("Redo: " + ucet.toString());