Monday, April 2, 2007

Undo / Redo galimybės programoje

Šiuo metu kuriame paprastą biuro programą.

Reikia sukurti tokius langus: mokymo dalyko langą, ir mokymo pamokų langą.
Be visų duomenų įvedimo laukų, langas turi turėti mygtukus išsaugoti ir uždaryti. Jei paspaudžiamas mygtukas išsaugoti, tai visi esami pakeitimai išsaugomi. Jei paspaudžiamas uždaryti, tai neišsaugoti pakeitimai turi būti atmesti ir langas uždarytas. Išsaugoti pakeitimai turi likti nepakitę.

Registruojant arba redaguojant mokymo pamokas, visi pakeitimai paspaudus mygtuką "išsaugoti" turi būti išsaugoti tik atmintyje. Į duomenų bazę pakeitimai siunčiami tuomet, kai saugomas pats dalykas.


Taigi, klausimas (labai jau daug išsiplėčiau apie probleminę sritį):
Kaip galima kokybiškai realizuoti Undo mechanizmą?


Ieškojau porą dienų sprendimo. Kol kas atsakymas matau yra toks: Memento design pattern'as.

Gerą pavyzdį radau čia:
Generic undo redo

Autorius siūlo tokį sprendimį: "an object that is capable of restoring a target object to one of its previous states".

Pavyzdys su c# būtų toks:

interface IMemento
{
///
/// Restores target to the state memorized by this memento.
///

///
/// A memento of the state before restoring
///

IMemento Restore(T target);
}



Sprendimas su Memento būtų turėti kiekvienam veiksmui atstatymo veiksmą. Atstatymo sprendimai gali būti dviejų rūšių -
1 - Atsatyti objektą darant jo deep-copy, ir vėliau originalą pakeičiant kopija
2 - Kiekvienam objekto keitimo veiksmui generuoti anti-veiksmą, t.y. veiksmą, atsatantį objektą į buvusią būseną. Tokių veiksmų būtų dvi rūšys - properčių atstatymas, ir ryšių atstatymas (objekto kūrimo ir šalinimo atveju).

Aš labiau linkstu prie antrojo varianto. Daryti objekto deep-copy gali sukelti daug problemų dėl ryšių. Atsatant objektus naudojant anti-veiksmus, viskas vyktų atvirkštine tvarka, negu tuomet, kai objektas buvo keičiamas.

Šiandieną bandysim implementuoti tokį sprendimą, žiūrėsim, kas gausis

2 comments:

Dalius said...

Gerai jei veiksmas turi atvirkštinį veiksmą, o jeigu ne? Pvz.: loseless suspaudinimas. Aišku čia pavyzdys elementarus, bet įtariu, kad kodo atžvilgiu galima situacija. Tam tikra prasme deep-copy tau teks kartais vistiek daryti ;)

Darius Damalakas said...

Matyt sumaišei su "lossy data compression". Lossless atveju, viską galima gražiai atsatyti įvykdžius priešingą algoritmą.

Bet šiaip sutinku, kad copy (deep, arba ne deep) reikės kartais daryti.

Čia yra klausimas, kaip suprojektuoti undo darymą.

Pirmas sprendimas, kuris šauna į galvą - yra IRestoreAction.
Tuomet kiekvienam skirtingui action'ui atskiras implementavimo variantas.

RestoreFromDeepCopy, RestoreFromShallowCopy, RestoreProperty, RestoreRelation
ir pan.

Kiekviens iš tų action padarytų su reikiamu objektu kas jam tinka.