Memento mönster
Mementomönstret är ett mjukvarudesignmönster som exponerar ett objekts privata interna tillstånd . Ett exempel på hur detta kan användas är att återställa ett objekt till dess tidigare tillstånd (ångra via återställning), ett annat är versionshantering, ett annat är anpassad serialisering.
Mementomönstret implementeras med tre objekt: upphovsmannen , en vaktmästare och ett minnesmärke . Upphovsmannen är något objekt som har ett internt tillstånd. Vaktmästaren ska göra något mot upphovsmannen, men vill kunna ångra förändringen. Vaktmästaren ber först upphovsmannen om ett minnesobjekt. Sedan gör den vilken operation (eller sekvens av operationer) den än skulle göra. För att återgå till tillståndet före operationerna, returnerar det minnesobjektet till upphovsmannen. Själva minnesobjektet är ett ogenomskinligt föremål (ett som vaktmästaren inte kan eller bör ändra). När du använder det här mönstret bör man vara försiktig om upphovsmannen kan ändra andra objekt eller resurser - minnesmönstret fungerar på ett enda objekt.
Klassiska exempel på mementomönstret inkluderar en pseudoslumptalsgenerator (varje konsument av PRNG fungerar som en vaktmästare som kan initiera PRNG (upphovsmannen) med samma frö (memento) för att producera en identisk sekvens av pseudoslumptal) och staten i en finita tillståndsmaskin.
Översikt
Mementos designmönster är ett av de tjugotre välkända GoF-designmönstren som beskriver hur man löser återkommande designproblem för att designa flexibel och återanvändbar objektorienterad programvara, det vill säga objekt som är lättare att implementera, ändra, testa och återanvändning. Memento-mönstret skapades av Noah Thompson, David Espiritu och Dr Drew Clinkenbeard för tidiga HP-produkter.
Vilka problem kan Mementos designmönster lösa?
- Det interna tillståndet för ett objekt bör sparas externt så att objektet kan återställas till detta tillstånd senare.
- Objektets inkapsling får inte kränkas.
Problemet är att ett väldesignat objekt är inkapslat så att dess representation (datastruktur) är dold inuti objektet och inte kan nås från utsidan av objektet.
Vilken lösning beskriver Mementos designmönster?
Gör ett objekt (upphovsman) själv ansvarigt för
- spara dess interna tillstånd till ett (minnes)objekt och
- återställa till ett tidigare tillstånd från ett (memento) objekt.
Endast upphovsmannen som skapade ett minne får åtkomst till det.
En klient (vaktmästare) kan begära ett minne från upphovsmannen (för att spara upphovsmannens interna tillstånd) och skicka ett minne tillbaka till upphovsmannen (för att återställa till ett tidigare tillstånd).
Detta gör det möjligt att spara och återställa det interna tillståndet för en upphovsman utan att bryta mot dess inkapsling.
Se även UML-klassen och sekvensdiagrammet nedan.
Strukturera
UML klass och sekvensdiagram
I ovanstående UML-klassdiagram hänvisar Caretaker
-klassen till Originator
-klassen för att spara ( createMemento()
) och återställa ( restore(memento) )
upphovsmannens interna tillstånd. Klassen Originator implementerar (1) createMemento()
genom att skapa och returnera ett Memento-
objekt som lagrar upphovsmannens aktuella interna tillstånd och (2) restore(memento)
genom att återställa tillståndet från det godkända Memento
-
objektet.
UML - sekvensdiagrammet visar körtidsinteraktionerna: (1) Sparar upphovsmannens interna tillstånd: Caretaker
-objektet anropar createMemento()
på Originator-
objektet, vilket skapar ett Memento-
objekt, sparar dess nuvarande interna tillstånd ( setState()
), och returnerar minnet till vaktmästaren
. _
(2) Återställa upphovsmannens interna tillstånd: Vaktmästaren anropar
restore (memento)
på Originator-
objektet och specificerar Memento-
objektet som lagrar tillståndet som ska återställas. Upphovsmannen får tillståndet ( getState()
) från Memento
för att ställa in sitt eget tillstånd .
Java exempel
Följande Java-program illustrerar "ångra" användningen av minnesmönstret.
importera java.util.List ; importera java.util.ArrayList ; klass Upphovsman { privat strängtillstånd ; _ // Klassen kan också innehålla ytterligare data som inte är en del av // -tillståndet sparat i minnet.. public void set ( String state ) { this . tillstånd = tillstånd ; System . ut . println ( "Originator: Ange tillstånd till " + tillstånd ); } public Memento saveToMemento () { System . ut . println ( "Upphov: Spara i minnet." ); returnera nytt minne ( detta .tillstånd ) ; } public void restoreFromMemento ( Memento memento ) { detta . tillstånd = memento . getSavedState (); System . ut . println ( "Originator: Tillstånd efter återställning från Memento: " + tillstånd ); } public static class Memento { private final String state ; public Memento ( String stateToSave ) { state = stateToSave ; } // endast tillgänglig för yttre klass privat String getSavedState () { return state ; } } } class Caretaker { public static void main ( String [ ] args ) { List < Originator . Memento > savedStates = new ArrayList < Originator . Memento > (); Upphovsman upphovsman = ny upphovsman (); upphovsman . set ( "State1" ); upphovsman . set ( "State2" ); sparade tillstånd . add ( upphovsman . saveToMemento ()); upphovsman . set ( "State3" ); // Vi kan begära flera minnen och välja vilket vi ska gå tillbaka till. sparade tillstånd . add ( upphovsman . saveToMemento ()); upphovsman . set ( "State4" ); upphovsman . restoreFromMemento ( savedStates . get ( 1 )); } }
Utgången är:
Upphovsman: Inställning av tillstånd till tillstånd1 Upphovsman: Inställning av tillstånd till tillstånd2 Upphovsman: Sparar till minne. Upphovsman: Ställer in tillstånd till State3 Upphovsman: Sparar till Memento. Upphovsman: Ställer in tillstånd till State4 Originator: Tillstånd efter återställning från Memento: Tillstånd3
Det här exemplet använder en sträng som tillstånd, vilket är ett oföränderligt objekt i Java. I verkliga scenarier kommer staten nästan alltid att vara ett föränderligt objekt, i vilket fall en kopia av staten måste göras.
Det måste sägas att den visade implementeringen har en nackdel: den deklarerar en intern klass. Det skulle vara bättre om denna minnesstrategi kunde tillämpas på mer än en upphovsman.
Det finns huvudsakligen tre andra sätt att uppnå Memento:
- Serialisering.
- En klass som deklareras i samma paket.
- Objektet kan också nås via en proxy, som kan uppnå valfri spara/återställningsoperation på objektet.
C# exempel
Mementomönstret tillåter en att fånga det interna tillståndet av ett objekt utan att bryta inkapslingen så att man senare kan ångra/återställa ändringarna om så krävs. Här kan man se att mementoobjektet faktiskt används för att återställa de ändringar som gjorts i objektet.
class Memento { privat skrivskyddad sträng savedState ; privat Memento ( sträng stateToSave ) { savedState = stateToSave ; } public class Originator { privat strängtillstånd ; _ // Klassen kan också innehålla ytterligare data som inte är en del av //-tillståndet sparat i minnet. public void Set ( strängtillstånd ) { Console . _ WriteLine ( "Originator: Ange tillstånd till " + tillstånd ); detta . tillstånd = tillstånd ; } public Memento SaveToMemento () { Console . WriteLine ( "Originator: Saving to Memento." ) ; returnera nytt Memento ( tillstånd ); } public void RestoreFromMemento ( Memento memento ) { state = memento . savedState ; Konsol . WriteLine ( "Originator: Status efter återställning från Memento: " + state ); } } } class Caretaker { static void Main ( string [ ] args ) { var savedStates = new List < Memento >(); var originator = nytt minne . Upphovsman (); upphovsman . Set ( "State1" ); upphovsman . Set ( "State2" ); sparade tillstånd . Lägg till ( upphovsman . SaveToMemento ()); upphovsman . Set ( "State3" ); // Vi kan begära flera minnen och välja vilket vi ska gå tillbaka till. sparade tillstånd . Lägg till ( upphovsman . SaveToMemento ()); upphovsman . Set ( "State4" ); upphovsman . RestoreFromMemento ( savedStates [ 1 ]); } }
Python exempel
""" Memento mönster exempel. """ klass Memento : def __init__ ( self , state ) -> None : self . _state = state def get_saved_state ( själv ): returnera själv . _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: Status efter återställning från Memento:" , self . _state ) saved_states = [] originator = Upphovsman () upphovsman . set ( "State1" ) upphovsman . set ( "State2" ) saved_states . append ( upphovsman . save_to_memento ()) upphovsman . set ( "State3" ) saved_states . append ( upphovsman . save_to_memento ()) upphovsman . set ( "State4" ) upphovsman . restore_from_memento ( sparade_tillstånd [ 1 ])
Javascript exempel
0
// Mementomönstret används för att spara och återställa tillståndet för ett objekt. // Ett minne är en ögonblicksbild av ett objekts tillstånd. var Memento = { // Namnområde: Memento savedState : null , // Det sparade tillståndet för objektet. save : function ( state ) { // Spara ett objekts tillstånd. detta . savedState = tillstånd ; }, restore : function () { // Återställ tillståndet för ett objekt. lämna tillbaka detta . savedState ; } }; // Upphovsmannen är objektet som skapar minnet. // definierar en metod för att spara tillståndet i ett minne. var Originator = { // Namnområde: Originator state : null , // Status som ska lagras // Skapar en ny originator med initialtillståndet null createMemento : function () { return { state : this . tillstånd // Tillståndet kopieras till minnet. }; }, setMemento : function ( memento ) { // Ställer in tillståndet för upphovsmannen från ett minne till detta . tillstånd = memento . tillstånd ; } }; // Vaktmästaren lagrar minnen av objekten och // tillhandahåller operationer för att hämta dem. var Caretaker = { // Namnområde: Caretaker- minnen : [], // Minnen av objekten. addMemento : function ( memento ) { // Lägg till ett minne till samlingen. detta . minnen . push ( minne ); }, getMemento : function ( index ) { // Få ett minne från samlingen. lämna tillbaka detta . minnen [ index ]; } }; var action_step = "Foo" ; // Åtgärden som ska utföras/objekttillståndet som ska lagras. var action_step_2 = "Bar" ; // Åtgärden som ska utföras/objekttillståndet som ska lagras. // ställ in initialtillståndet Originator . state = action_step ; Vaktmästare . addMemento ( upphovsman . createMemento ()); // spara tillståndet till historikkonsolen . log ( "Initial State: " + Originator . state ); // Foo // ändra tillståndet Originator . state = action_step_2 ; Vaktmästare . addMemento ( upphovsman . createMemento ()); // spara tillståndet till historikkonsolen . log ( "Tillstånd efter ändring: " + Upphovsman . tillstånd ); // Bar // återställ den första tillståndet - ångra Originator . setMemento ( Vaktmästare . getMemento ( )); konsol . log ( "Tillstånd efter ångra: " + Upphovsman . tillstånd ); // Foo // återställ det andra tillståndet - gör om Originator . setMemento ( vaktmästare . getMemento ( 1 )); konsol . log ( "State After Redo: " + Originator . state ); // Bar
externa länkar
- Beskrivning av Memento Pattern i Ada
- Memento UML Class Diagram med C#- och .NET-kodexempel
- Självstudie för SourceMaking
- Memento Design Pattern med Java