Prototype Pattern

Clonomat

Sometimes the creation of new objects is time and resource intensive. You could create a thousand objects during the program initialization and park them on a queue. The object would then be ready when needed. And looking at the Garbage Collection process in more detail, you may notice that the Generation has probably changed by then. Your object requires less processor time the older it becomes.

But this is not the topic today. It just has a similar idea. We are talking about time and/or resource intensive object creation.

The Prototype Pattern is used for cloning objects. Cloning can be substantially faster than initializing objects from scratch. Let’s say object A did load a lot of data from a file. You don’t have to do the same for object B. Simply copy object A and amend some fields.

Here is the pattern:

public interface IChocolateBar {
  IChocolateBar Clone();
} // interface

public class MintChocolateBar : IChocolateBar {
  public readonly int ObjectNumber;
  public MintChocolateBar(int xObjectNumber) { ObjectNumber = xObjectNumber; }

  public IChocolateBar Clone() { return MemberwiseClone() as MintChocolateBar; } 
} // class

public class DarkChocolateBar : IChocolateBar {
  public readonly int ObjectNumber;
  public DarkChocolateBar(int xObjectNumber) { ObjectNumber = xObjectNumber; }

  public IChocolateBar Clone() { return MemberwiseClone() as DarkChocolateBar; } 
} // class

public class CloneFactory {
  public IChocolateBar get(IChocolateBar xChocolateBar) { return xChocolateBar.Clone(); } 
} // class

The pattern is not really satisfying. Is there something better? C# offers the ICloneable interface.

public class Shortcut : ICloneable  {
  public readonly int ObjectNumber;
  public Shortcut(int xObjectNumber) { ObjectNumber = xObjectNumber; }

  public object Clone() { return MemberwiseClone(); } 
} // class

The problem with this interface is the missing generic type. In fact the use is obsolete and Microsoft does not recommend it anymore. We therefore build our own implementation.

public class Shortcut2 {
  public readonly int ObjectNumber;
  public Shortcut2(int xObjectNumber) { ObjectNumber = xObjectNumber; }

  public Shortcut2 ShallowCopy() { return MemberwiseClone() as Shortcut2; } 
} // class

Using an interface like

interface IShallowCopy<T> {
  T IShallowCopy();
} // interface 

public class Shortcut3<T> : IShallowCopy<T> {
  public readonly int ObjectNumber;
  public Shortcut3(int xObjectNumber) { ObjectNumber = xObjectNumber; }

  public T ShallowCopy() { return MemberwiseClone() as T; } 
} // class

is nonsense. You cannot compile and instantiate this.
What else could we do? A static clone method quickly leads us back to a factory similar type. Hence we end up with something that we were having at the very beginning today. Just keep your solutions generic and you should be fine with this pattern.

MemberwiseClone()

We were using MemberwiseClone() to keep the example source code simple. The following is important to know:

MemberwiseClone() is a shallow copy. If a field of the copied object is a reference type, the reference is copied but the referred object is not; therefore, the original object and its clone refer to the same object.

To avoid the same references in the clone, you have to write your own Deep Copy or Lazy Copy. Your clone method must clone the object and objects inside the object.

A practical example:

Stock exchange orders need to be sent with a minimal delay. The fight for nanoseconds is tremendous. Companies even shorten the physical server cables to the exchanges in order to increase speed. One foot in length equals roughly one nanosecond these days.
The object creation for orders takes too long in the proprietary business. It is meaningful to generate orders before any decision is made. The next order sits in a queue of clones. When the machine decides to trade, then the order is taken from that queue and a few fields are populated/overridden. These are eg. trade size and price. There is no memory allocation at this late stage. As said, each nanosecond counts.

Full source code

Notice that

Console.WriteLine("ObjectNumber = " + lMintClone.ObjectNumber); 

prints the same number as the original object. There are two objects at two locations in the RAM. Therefore the HashCode is different. Still the field content is the same.

using System;

namespace PrototypePattern {

  public interface IChocolateBar {
    IChocolateBar Clone();
  } // interface

  public class MintChocolateBar : IChocolateBar {
    public readonly int ObjectNumber;
    public MintChocolateBar(int xObjectNumber) { ObjectNumber = xObjectNumber; }

    public IChocolateBar Clone() { return MemberwiseClone() as MintChocolateBar; }

  } // class

  public class DarkChocolateBar : IChocolateBar {
    public readonly int ObjectNumber;
    public DarkChocolateBar(int xObjectNumber) { ObjectNumber = xObjectNumber; }

    public IChocolateBar Clone() { return MemberwiseClone() as DarkChocolateBar; }

  } // class

  public class CloneFactory {
    public IChocolateBar get(IChocolateBar xChocolateBar) { return xChocolateBar.Clone(); }
  } // class


  // IClonable is non-generic
  public class Shortcut : ICloneable {
    public readonly int ObjectNumber;
    public Shortcut(int xObjectNumber) { ObjectNumber = xObjectNumber; }

    public object Clone() { return MemberwiseClone(); } 
  } // class

  public class Shortcut2 {
    public readonly int ObjectNumber;
    public Shortcut2(int xObjectNumber) { ObjectNumber = xObjectNumber; }

    public Shortcut2 ShallowCopy() { return MemberwiseClone() as Shortcut2; } 
  } // class

  class Program {

    static void Main(string[] args) {
      CloneFactory lCloneFactory = new CloneFactory();
      MintChocolateBar lMint = new MintChocolateBar(1);
      MintChocolateBar lMintClone = lCloneFactory.get(lMint) as MintChocolateBar;
      Console.WriteLine("Original object: ");
      Console.WriteLine("HashCode = " + lMint.GetHashCode());
      Console.WriteLine("ObjectNumber = " + lMint.ObjectNumber);
      Console.WriteLine();
      Console.WriteLine("Clone: ");
      Console.WriteLine("HashCode = " + lMintClone.GetHashCode());
      Console.WriteLine("ObjectNumber = " + lMintClone.ObjectNumber);  // !!!
      Console.WriteLine();
      Console.WriteLine("Are the objects the same? " + (lMint == lMintClone));

      DarkChocolateBar lDark = new DarkChocolateBar(2);
      DarkChocolateBar lDarkClone = lCloneFactory.get(lDark) as DarkChocolateBar;
      Console.WriteLine();
      Console.WriteLine();
      Console.WriteLine();
      Console.WriteLine("Dark chocolate: ");
      Console.WriteLine("Are the objects the same? " + (lMint == lMintClone));


      // old school
      Shortcut lShort = new Shortcut(3);
      Shortcut lShortClone = lShort.Clone() as Shortcut;
      Console.WriteLine();
      Console.WriteLine("ICloneable: ");
      Console.WriteLine("Are the objects the same? " + (lShort == lShortClone));

      Console.ReadLine();
    } //

  } // class

} // namespace

example output:
Original object:
HashCode = 62125865
ObjectNumber = 1

Clone:
HashCode = 44200505
ObjectNumber = 1

Are the objects the same? False

Dark chocolate:
Are the objects the same? False

ICloneable:
Are the objects the same? False

Wikipedia

Advertisements

About Bastian M.K. Ohta

Happiness only real when shared.

Posted on July 1, 2014, in Basic, C#, Creational Patterns, Programming Patterns and tagged , , , , , , . Bookmark the permalink. Leave a comment.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: