Pattern Name:

Prototype Pattern

Short Description:

Clone or copy initialized instances

Usage:

Easy pattern, usage depends on the preferred software design, provides an alternative to the other creational patterns that are mainly based on external classes for creation

Complexity:

1 / 5

UML Class Diagram:

image

Explanation:

  • The abstract prototype class defines the interface that contains a clone method for cloning itself.
    public abstract class ProductPrototype
    {
        public decimal Price { get; set; }

        public ProductPrototype(decimal price)
        {
            Price = price;
        }

        public abstract ProductPrototype Clone();
    }
  • The inherited classes implement the clone function. Note that C# provides two different ways of cloning an object: by shallow copy (only top level objects) or by deep copy (all objects). 
  • You could also manually create a new object and set its values with the values of the original object but since C# already implements everything necessary I advise using that.
    public class Milk : ProductPrototype
    {
        public Milk(decimal price)
            : base(price)
        {

        }

        public override ProductPrototype Clone()
        {
            // Shallow Copy: only top-level objects are duplicated
            return (ProductPrototype) this.MemberwiseClone();

            // Deep Copy: all objects are duplicated
            //return (ProductPrototype)this.Clone();
        }
    }

    public class Bread : ProductPrototype
    {
        public Bread(decimal price)
            : base(price)
        {

        }

        public override ProductPrototype Clone()
        {
            // Shallow Copy: only top-level objects are duplicated
            return (ProductPrototype)this.MemberwiseClone();

            // Deep Copy: all objects are duplicated
            //return (ProductPrototype)this.Clone();
        }
    }
  • The client class does not create new objects itself. Instead it asks the objects to clone themselves when needed. The cloned objects are perfect copies and contain all values of the original objects depending on the shallow or deep copy approach (see above).
    public class Supermarket
    {
        private Dictionary<string, ProductPrototype> _productList = new Dictionary<string, ProductPrototype>();

        public void AddProduct(string key, ProductPrototype productPrototype)
        {
            _productList.Add(key, productPrototype);
        }

        public ProductPrototype GetProduct(string key)
        {
            var product = _productList[key];
            return product.Clone();
        }
    }
  • You may also use the ICloneable interface that already exists in C#
    public class CloneableProduct : ICloneable
    {
        public decimal Price { get; set; }

        public CloneableProduct(decimal price)
        {
            Price = price;
        }

        public object Clone()
        {
            // Shallow Copy: only top-level objects are duplicated
            return this.MemberwiseClone();

            // Deep Copy: all objects are duplicated
            //return this.Clone();
        }
    }
  • In the last step we add some code to test the software design and the Prototype implementation.
    private static void Prototype()
    {
        // Language agnostic solution
        var supermarket = new Supermarket();           

        supermarket.AddProduct("Milk", new Milk(0.89m));
        supermarket.AddProduct("Bread", new Bread(1.10m));

        decimal sourcePrice;
        decimal currentPrice;

        var clonedMilk = (Milk) supermarket.GetProduct("Milk");
        clonedMilk.Price = 1;
        sourcePrice = supermarket.GetProduct("Milk").Price;
        currentPrice = clonedMilk.Price;
        Console.WriteLine(String.Format("{0} | {1}", sourcePrice, currentPrice));

        var clonedBread = (Bread)supermarket.GetProduct("Bread");
        clonedBread.Price = 2;
        sourcePrice = supermarket.GetProduct("Bread").Price;
        currentPrice = clonedBread.Price;
        Console.WriteLine(String.Format("{0} | {1}", sourcePrice, currentPrice));

        // C# specific solution using the ICloneable interface
        var cloneableProduct = new CloneableProduct(100);
        var clonedProduct = (CloneableProduct) cloneableProduct.Clone();
        clonedProduct.Price = 200;
        sourcePrice = cloneableProduct.Price;
        currentPrice = clonedProduct.Price;
        Console.WriteLine(String.Format("{0} | {1}", sourcePrice, currentPrice));

        Console.ReadKey();
    }
  • When running the example you can see that everything is working as expected and that the correct classes are instantiated during runtime.

image

Last edited May 7, 2011 at 6:44 PM by JasonOliveira, version 3

Comments

cluedtke Dec 14, 2012 at 8:31 PM 
These are great reference guides. Thanks for all your efforts!

One thing I did notice.. Console.WriteLine() has an overload:
WriteLine(string format, params object[] arg);

You can use that and omit String.Format() in:
Console.WriteLine(String.Format("{0} | {1}", sourcePrice, currentPrice));

Thanks again.

w3schools Jul 16, 2012 at 3:28 AM 
nice