Singleton and Prototype Patterns


Singleton and Prototype patterns fall under Creational Design Patterns which deal with object creation.

Singleton - The purpose of a Singleton pattern is to ensure that one and only instance of the class is present at any time. We usually create the instance of the object on demand i.e. delay loading and then ensure that once it is created, no more instances are allowed to be created.

A classical example would be a menu in a Windows application. The menu is common to all pages so we ensure it is not recreated again and again.

The basic fundamental of a singleton is to have a class with a private constructor and expose a static getter property which will control the object creation; upon initial access, the object has to be created and then for subsequent access, the same object is returned. For thread safety purposes, we can use a lock.

Prototype pattern - The intent behind the usage of a Prototype pattern is for creation of an object clone.

The cloning again falls under two categories: shallow and deep. A shallow copy is returned by using MemberwiseClone .Net method. A shallow copy copies only the elements, whether they are reference types or value types, but it does not copy the objects that the references refer to. The references in the new object point to the same objects that the references in the original object points to. In contrast, a deep copy of an object copies the elements and everything directly or indirectly referenced by the elements.

The following example will explain the difference between shallow and deep clone clearly.

Code for the explanation of both the patterns :

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace DesignPatterns
{
    public class CreationalPatterns
    {
        public static void Main()
        {
            // Prototype test
            ProtoType p = new ProtoType();
            TestA a11 = new TestA();
            a11.myList.Add("1");
            a11.myList.Add("2");
            a11.name = "Testing";
            TestB b11 = new TestB();
            b11.= 10;
            a11.b1 = b11;
            p.a1 = a11;
            ProtoType p1 = p.ShallowClone();
            ProtoType p2 = p.DeepClone();
            // Both have values copied now change a bit
            a11.myList.Add("3");
            a11.name = "Testing Again after adding 3";
            // Deep copy will show original values whereas
            // Shallow copy shows current values
            string nameA1 = p1.a1.name;
            string nameA2 = p2.a1.name;
            // Singleton Test
            // At any moment, only and one instance of the Menu is created
            MenuInstance menu = MenuInstance.myMenu;
            Console.ReadLine();
        }
    }
 
    // Singleton class - only one instance
    // Has private constructor and a property is exposed
    // when trying to access the property, check is made if instance
    // is already created and if yes, it is returned else instance is created
    // and then returned. This is called lazy instantiation.
    // Lock is used to prevent thread synchronication issues...if two threads
    // try to create instances simultaneously
    public class MenuInstance
    {
        private static MenuInstance _myMenu;
        private static object myLock = new object();
        private MenuInstance()
        {
        }
        public static MenuInstance myMenu
        {
            get
            {
                // Lock is acquired each time we try to access the instance
                lock (myLock)
                {
                    if (_myMenu == null)
                    {
                        _myMenu = new MenuInstance();
                    }
                }
                return _myMenu;
            }
        }
    }
 
    ///
    /// Prototype pattern implemenatation
    ///
    [Serializable]
    public class ProtoType
    {
        public TestA a1 { getset; }
        // Creates a shallow copy
        // Same copy of the referenced object is taken.
        // The original object and its clone refer to the same object.
        public T ShallowClone()
        {
            return (T)this.MemberwiseClone(); ;
        }
 
        // Creates a deep copy
        // A new copy of the referenced object is created
        // The original object and its clone refer to different objects.
        public T DeepClone()
        {
            MemoryStream stream = new MemoryStream();
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, this);
            stream.Seek(0SeekOrigin.Begin);
            T copy = (T)formatter.Deserialize(stream);
            stream.Close();
            return copy;
        }
    }
 
    [Serializable]
    public class TestA
    {
        public List myList = new List();
        public string name = string.Empty;
        public TestB b1;
    }
    [Serializable]
    public class TestB
    {
        public int i;
    }
}