AddThis Social Bookmark Button

Print

Object Serialization with the Memento Pattern
Pages: 1, 2

The form class for this application is shown in Example 3.



Example 3. The application's form class


using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;

namespace MyCSharp
{
  /// <summary>
  /// Summary description for Form1.
  /// </summary>
  public class Form1 : System.Windows.Forms.Form
  {
    private System.Windows.Forms.MainMenu mainMenu1;
    private System.Windows.Forms.MenuItem fileMenuItem;
    private System.Windows.Forms.MenuItem fileNewMenuItem;
    private System.Windows.Forms.MenuItem fileOpenMenuItem;
    private System.Windows.Forms.MenuItem fileSaveMenuItem;
    private System.Windows.Forms.MenuItem fileSaveAsMenuItem;
    private System.Windows.Forms.MenuItem fileExitMenuItem;
    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.Container components = null;

    private Point startPoint;
    private String filename;

    public Form1()
    {
      //
      // Required for Windows Form Designer support
      //
      InitializeComponent();

      //
      // TODO: Add any constructor code after InitializeComponent call
      //
    }

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    protected override void Dispose( bool disposing )
    {
      if( disposing )
      {
        if (components != null) 
        {
          components.Dispose();
        }
      }
      base.Dispose( disposing );
    }

		#region Windows Form Designer generated code
    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
      this.mainMenu1 = new System.Windows.Forms.MainMenu();
      this.fileMenuItem = new System.Windows.Forms.MenuItem();
      this.fileNewMenuItem = new System.Windows.Forms.MenuItem();
      this.fileOpenMenuItem = new System.Windows.Forms.MenuItem();
      this.fileSaveMenuItem = new System.Windows.Forms.MenuItem();
      this.fileSaveAsMenuItem = new System.Windows.Forms.MenuItem();
      this.fileExitMenuItem = new System.Windows.Forms.MenuItem();
      // 
      // mainMenu1
      // 
      this.mainMenu1.MenuItems.AddRange(
	      new System.Windows.Forms.MenuItem[] {
                                          this.fileMenuItem});
      // 
      // fileMenuItem
      // 
      this.fileMenuItem.Index = 0;
      this.fileMenuItem.MenuItems.AddRange(
	       new System.Windows.Forms.MenuItem[] {
                                           this.fileNewMenuItem,
                                           this.fileOpenMenuItem,
                                           this.fileSaveMenuItem,
                                           this.fileSaveAsMenuItem,
                                           this.fileExitMenuItem});
      this.fileMenuItem.Text = "File";
      // 
      // fileNewMenuItem
      // 
      this.fileNewMenuItem.Index = 0;
      this.fileNewMenuItem.Shortcut = 
	       System.Windows.Forms.Shortcut.CtrlN;
      this.fileNewMenuItem.Text = "&New";
      this.fileNewMenuItem.Click += new System.EventHandler(
	       this.fileNewMenuItem_Click);
      // 
      // fileOpenMenuItem
      // 
      this.fileOpenMenuItem.Index = 1;
      this.fileOpenMenuItem.Shortcut = 
	       System.Windows.Forms.Shortcut.CtrlO;
      this.fileOpenMenuItem.Text = "&Open";
      this.fileOpenMenuItem.Click += new System.EventHandler(
	       this.fileOpenMenuItem_Click);
      // 
      // fileSaveMenuItem
      // 
      this.fileSaveMenuItem.Index = 2;
      this.fileSaveMenuItem.Shortcut = 
	       System.Windows.Forms.Shortcut.CtrlS;
      this.fileSaveMenuItem.Text = "&Save";
      this.fileSaveMenuItem.Click += new System.EventHandler(
	       this.fileSaveMenuItem_Click);
      // 
      // fileSaveAsMenuItem
      // 
      this.fileSaveAsMenuItem.Index = 3;
      this.fileSaveAsMenuItem.Text = "Save &As";
      this.fileSaveAsMenuItem.Click += new System.EventHandler(
	       this.fileSaveAsMenuItem_Click);
      // 
      // fileExitMenuItem
      // 
      this.fileExitMenuItem.Index = 4;
      this.fileExitMenuItem.Text = "E&xit";
      this.fileExitMenuItem.Click += new System.EventHandler(
	       this.fileExitMenuItem_Click);
      // 
      // Form1
      // 
      this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
      this.BackColor = System.Drawing.Color.White;
      this.ClientSize = new System.Drawing.Size(292, 273);
      this.Menu = this.mainMenu1;
      this.Name = "Form1";
      this.Text = "Form1";
      this.MouseDown += new System.Windows.Forms.MouseEventHandler(
	       this.this_MouseDown);
      this.MouseUp += new System.Windows.Forms.MouseEventHandler(
	       this.this_MouseUp);

    }
		#endregion

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main() 
    {
      Application.Run(new Form1());
    }

    private void fileExitMenuItem_Click(object sender, 
	     System.EventArgs e)
    {
      this.Close(); 
    }

    private void fileNewMenuItem_Click(object sender, 
	     System.EventArgs e)
    {
      this.Controls.Clear();
    }

    private void fileOpenMenuItem_Click(object sender, 
	     System.EventArgs e)
    {
      OpenDocument();
    }

    private void fileSaveMenuItem_Click(object sender, 
	     System.EventArgs e)
    {
      Save();
    }

    private void fileSaveAsMenuItem_Click(object sender, 
	     System.EventArgs e)
    {
      SaveAs();
    }

    private void OpenDocument()
    {
      OpenFileDialog openFileDialog = new OpenFileDialog();

      if (openFileDialog.ShowDialog() == DialogResult.OK)
      {
        filename = openFileDialog.FileName;
        Stream myStream = openFileDialog.OpenFile();
        if (myStream != null)
        {
          this.Controls.Clear();
          IFormatter formatter = new BinaryFormatter();
          ArrayList mementos = (ArrayList) formatter.Deserialize(
		       myStream);
          IEnumerator mementoEnum = mementos.GetEnumerator();
          while (mementoEnum.MoveNext())
          {
            Memento memento = (Memento) mementoEnum.Current;
            Ellipse ellipse = new Ellipse();
            ellipse.SetMemento(memento);
            this.Controls.Add(ellipse);
          }
          myStream.Close();
          this.Refresh();
        }
      }
    }

    private bool Save() 
    {
      if (filename==null) 
      {
        return SaveAs();
      }
      else {
        Stream myStream ;
        myStream = File.OpenWrite(filename);
        if (myStream != null)
        {
          IFormatter formatter = new BinaryFormatter();
          // serialize shapes
          ArrayList mementos = new ArrayList();
          foreach (Control ctl in this.Controls)
          {
            mementos.Add( ((Ellipse)ctl).CreateMemento()); 
          }
          formatter.Serialize(myStream, mementos);
          myStream.Close();
          return true;
          }
        }
      return false;
    }

    private bool SaveAs() 
    {
      SaveFileDialog saveFileDialog = new SaveFileDialog();
      if (saveFileDialog.ShowDialog() == DialogResult.OK)
      {
        filename = saveFileDialog.FileName;
        return Save();
      }
      return false;
    }

    private void this_MouseDown(object sender, 
	     System.Windows.Forms.MouseEventArgs e)
    {
      if (e.Button==System.Windows.Forms.MouseButtons.Left) 
      {
        startPoint = new Point(e.X, e.Y);
      }
    }

    private void this_MouseUp(object sender, 
	     System.Windows.Forms.MouseEventArgs e)
    {
      if (e.Button==System.Windows.Forms.MouseButtons.Left) 
      {
        Point endPoint = new Point(e.X, e.Y);
        this.Controls.Add(new Ellipse(GetRectangleFromPoints(
		     startPoint, endPoint))); 
      }
      this.Refresh();
    }

    private Rectangle GetRectangleFromPoints(Point p1, Point p2) 
    {
      int x1, x2, y1, y2;
      if (p1.X < p2.X) 
      {
        x1 = p1.X;
        x2 = p2.X;
      }
      else 
      {
        x1 = p2.X;
        x2 = p1.X;
      }

      if (p1.Y < p2.Y) 
      {
        y1 = p1.Y;
        y2 = p2.Y;
      }
      else 
      {
        y1 = p2.Y;
        y2 = p1.Y;
      }
      // x2 > x1 and y2 > y1
      return new Rectangle(x1, y1, x2 - x1, y2 - y1);
    }
  }

}

Serializing the Objects

Serializing the objects is done in the Save method. The part that does this job is as follows.


      if (myStream != null)
        {
          IFormatter formatter = new BinaryFormatter();
          // serialize shapes
          ArrayList mementos = new ArrayList();
          foreach (Control ctl in this.Controls)
          {
            mementos.Add( ((Ellipse)ctl).CreateMemento()); 
          }
          formatter.Serialize(myStream, mementos);
          myStream.Close();
          return true;
          }
        }

First, it constructs a BinaryFormatter object.


IFormatter formatter = new BinaryFormatter();

Then, the method instantiates an ArrayList method that will contains the mementos of each Ellipse object.


ArrayList mementos = new ArrayList();

Next, it uses a foreach statement to iterate all the controls in the form's Controls collection, cast each control to Ellipse, and add them to the mementos ArrayList.


          foreach (Control ctl in this.Controls)
          {
            mementos.Add( ((Ellipse)ctl).CreateMemento()); 
          }

To serialize, we use the Serialize method of the IFormatter interface.


formatter.Serialize(myStream, mementos);

Deserializing the Objects

Deserializing the previously-persisted objects happens in the OpenDocument method. Here is the part that does that.


        if (myStream != null)
        {
          this.Controls.Clear();
          IFormatter formatter = new BinaryFormatter();
          ArrayList mementos = (ArrayList) formatter.Deserialize(
		       myStream);
          IEnumerator mementoEnum = mementos.GetEnumerator();
          while (mementoEnum.MoveNext())
          {
            Memento memento = (Memento) mementoEnum.Current;
            Ellipse ellipse = new Ellipse();
            ellipse.SetMemento(memento);
            this.Controls.Add(ellipse);
          }
          myStream.Close();
          this.Refresh();
        }

It starts by constructing a BinaryFormatter object.


IFormatter formatter = new BinaryFormatter();

Then, it calls the Deserialize method of the Iformatter interface to deserialize the object, and casts it into ArrayList.


ArrayList mementos = (ArrayList) formatter.Deserialize(myStream);

Next, we need to reconstruct the Ellipse objects. For each object, we cast it to Memento and create an Ellipse object. Reconstruction happens when we call the SetMemento method of the Ellipse class.


            Memento memento = (Memento) mementoEnum.Current;
            Ellipse ellipse = new Ellipse();
            ellipse.SetMemento(memento);

Finally, we add the Ellipse object to the form's Controls collection.


this.Controls.Add(ellipse);

Summary

In this article, we have learned to use the memento design pattern to serialize objects which are otherwise unfit to be persisted. In the memento pattern, the original object is called the originator, and we add two methods to the originator's class: CreateMemento and SetMemento. The CreateMemento method constructs a Memento object, populates it with states that need to be persisted, and returns the Memento object. The SetMemento method does the opposite and is called to construct the original object.

Budi Kurniawan is a senior J2EE architect and author.


Return to .NET DevCenter