Memento Design Pattern

2 בMarch 2013

Memento Design Pattern

ממנטו בתרגום חפשי זאת מזכרת, מהות התבנית היא לשמור מצב של אובייקט\ים
לצורך שימוש\שחזור מאוחר יותר, ישנו דמיון מסוים ל
command
Pattern
במובן הזה שאתו מקובל לממש redo undo, רק שכאן אני לא משחזר פעולה אלא מצב נתון
של אובייקט, ויותר מזה
Memento יתמוך בשחזור מלא גם לאחר
סגירת התכנית. הדוגמא הטובה ביותר זה משחקי מחשב שמאפשרים לשמור מצבים באמצע שלב
ולחזור בדיוק לנקודה. בדרך כלל כשמעלים לדיון בדוט נט את ממנטו כתבנית עיצוב הפתרון
המוצא הוא באמצעות סריליזציה – כתיבה לזיכרון של אובייקטים. דוט נט יודע באמצעות
Reflection לבנות אובייקטים כאלה מתוך קובץ בינארי או XML. ונציג דוגמא בסיסית לתבנית.

נניח שיש לי משחק לוח שמיוצג ע”י מערך של כלי משחק (לצורך פשטות
הדוגמא אני משתמש במערך חד ממדי), אני יכול להגדיר בערך את הדבר הבא:

יש לי אובייקט מסוג כלי משחק (PlayTool) עם תיאור ופוינט (מיקום
על הלוח). והוא מממש מה שנקרא סריליזציה. התוספת מסוג
xmlInclude תאפשר לי לשמור גם יורשים
ספציפיים.

 [XmlInclude(typeof(BlackPlayTool))]
 [XmlInclude(typeof(WihtePlayTool))]
 [Serializable]
        public abstract class PlayTool
        {
            public string PlayTitle { getset; }
            public int Point_X { getset; }
            public int Point_Y { getset; }
            
            public PlayTool(string _title, int x, int y)
            {
                PlayTitle = _title; Point_X = x; Point_Y = y;
            }
        }

יש לי שני יורשים שמהווים מימוש קונקרטי לכלי משחק, (כרגע בלי תוספת
למבנה המוריש).

 public class BlackPlayTool : PlayTool
        {
            public BlackPlayTool(string _title, int _x, int _y)
                : base(_title, _x, _y)
            {
            }
        }
 
 
        public class WihtePlayTool : PlayTool
        {
            public WihtePlayTool(string _title, int _x, int _y)
                : base(_title, _x, _y)
            {
            }
        }

ויש לי אובייקט לוח משחק שמחזיק כאמור מערך של כלי משחק.

    public class Game
        {
          public  PlayTool[] State{ getset;}
            
            public Game()
            {
                State = new PlayTool[10];
            }
            public void setPLayToolPosition(PlayTool  tool, int pos)
            {
                if (pos < State.Length)
                {
                    if (State[pos] == null)
                    {
                        State[pos] = tool;
                    }
                }
            }
 
          public void PrintGameBoardState()
          {
            for (int i = 0; i < this.State.Length; i++)
                  if (State[i] != null)
                     Console.WriteLine(State[i].PlayTitle + " : " + i);
                   else
                       Console.WriteLine("no value in :" + i);                    
               }
            }
        }
         

הרעיון הוא כזה:  נניח שיש לי
כלים שחורים ולבנים ואני ממקם אותם לפי מיקום
X,Y לפי איזו לוגיקת משחק
שתבחרו, פשוט הוסיפו את הפונקציה שמחליטה מהי הלוגיקה.. מה שמעניין אותי זה לקחת
את המצב הנתון ולתת לו יכולת שמירה (צילום של לוח המשחק) שניתן יהיה לטעון בשלב
מאוחר יותר ולראות בדיוק את אותה התוצאה.. כרגע יש לי פונקציית הוספת ערך ללוח
המשחק, ופונקציית הדפסה שמצגה לי את ה”סטייט” הנוכחי, בדיוק את הסטייט
הזה ארצה לשמור באמצעות ידידינו
Memento.

אז אני מוסיף אובייקט נוסף וקורא לו Memento (מקורי אה?) ושם אחד ממנו
כפרופרטי בקלאס משחק.
מה שיודע
לעשות ממנטו במימוש שלי  זה ליצור עותק
מהלוח ול”סרלז” אותו, (המימוש הפשוט ביותר בדוט נט) ברגע שיש לי אובייקט
שעבר
Serialisation לזיכרון או לקובץ , יש לי
למעשה גיבוי למשחק.

 public class Memento
  { 
    public Game game { getset; }
    public PlayTool[] getMemento()
    {
     XmlSerializer deserializer = new XmlSerializer(game.GetType());
     PlayTool[] returnValue = null;
     using (TextReader textReader = new StreamReader("MementoData.txt"))
     {
       try
         {
          returnValue = (PlayTool[])deserializer.Deserialize(textReader);
          }
          catch (Exception e)
           {
               Console.WriteLine("error, " + e.InnerException);
           }
 
          return returnValue;
           }    
       }
 
      public void setMemento(Game gm)
      {
        XmlSerializer xmlSerializer = new XmlSerializer(gm.State.GetType());
        using (StreamWriter streamWriter = System.IO.File.CreateText
                                                       ("MementoData.txt"))
             {
                 xmlSerializer.Serialize(streamWriter, gm.State);
 
             }
      }
    }
 

 

לצורך ההפעלה אני מרחיב את לוח המשחק ומוסיף פונקציית שמירה ופונקציית
טעינה, והוא ייראה כך.  

 public class Game
        {
            public PlayTool[] State { getset;}
            public Memento G_memento { getset;}
           
            public Game()
            {
                State = new PlayTool[10];
                G_memento = new Memento();
            }
            public void setPLayToolPosition(PlayTool tool, int pos)
            {
                if (pos < State.Length)
                {
                    if (State[pos] == null)
                    {
                        State[pos] = tool;
                    }
                }
            }
 
            public void PrintGameBoardState()
            {
                for (int i = 0; i < this.State.Length; i++)
                {
                    if (State[i] != null)
                      Console.WriteLine(State[i].PlayTitle + " : " + i);
                    else
                        Console.WriteLine("no value in :" + i);
                }
            }
            public void save()
            {
                G_memento.setMemento(this);
            }
 
            public void load()
            {
                G_memento.game = this;
                this.State = G_memento.getMemento();
            }
        }

זהו ניתן לשמור משחק ואפילו אחרי כיבוי מחשב לטעון אותו מהזיכרון.

בצורה הנוכחית שמרנו לקובץ XML על היתרונות שבו, מיקום
ברירת המחדל של הקובץ הוא
bin/debug/file

הוסף תגובה
facebook linkedin twitter email

Leave a Reply

Your email address will not be published. Required fields are marked *

3 תגובות

  1. Shahar Eldad3 בMarch 2013 ב 0:15

    בהחלט מעניין – תודה

    Reply
  2. שחר.NET13 בMarch 2013 ב 15:07

    יפה אהבתי. נראה לי שמקובל יותר לשמור לזיכרון בינארי או Cash של מערכת הפעלה

    Reply
  3. orel3 בJuly 2016 ב 20:54

    אחלה הסבר! חבל שלקח לי קצת זמן עד שמצאתי את הבלוג הזה ..
    תודה רבה

    Reply