Design Patterns

Friday Jan 22nd 2016 by Peter Shaw
Share:

Specify concrete implementations of supplied objects using a prototypical instance leading to the creation of new objects by copying the underlying prototype.

Design Patterns: The Prototype Pattern

Pattern Type: Creational

Description

The prototype design pattern seeks to provide a single unitary interface to a single class type, all while allowing the underlying class type to be different.

This has a number of uses. For example, the GOF book illustrates the pattern through the use of a "Graphical Object," which can be used to create "Staves," "Notes," and "Time Signatures" in a music score editing application.

Underneath the hood, the client application can refer to everything as a "Graphical Object" that has a known set of methods and properties such as "Draw()" and "Colour," but is capable of drawing very different objects using just those known base elements.

Another interesting example of its use could lie in an electronic logic simulator, where user circuits built up from lower level components could be loaded into the application, and used as sub circuits in a larger design.

Each circuit, irrespective of its underlying type, could be instantiated and represented as a "SubCircuit" but have drastically different inner workings under the covers.

Diagram

Proto
Figure 1: A client/prototype diagram

Client: This is the consumer requesting the prototype object

Prototype: This is an interface or an abstractly defined class representing the single class type

Concrete Prototype: These are the actual implementations returned by the prototype master

Implementation in C#

To implement the prototype design pattern, you need an interface or abstract class. This abstract class is the "Class Type" that consumers of the prototype object will create instances of. In the following example, these will be "Car Objects."

The prototype itself, however, usually is just the "structure". The actual implementations are provided by actual classes that either follow a contract-based interface, or are abstracted from the super object; in this example, "Race Car" and "Saloon Car."

To start with, the main prototype may look something like the following:

namespace PrototypePattern
{
   public abstract class Car
   {
      public int TopSpeed { get; set; }
      public int EngineSize { get; set; }
      public string Colour { get; set; }

      public abstract Car Clone();
   }
}

Here, we stamp out the base class type, and make sure that the basic information we might hold is represented, and that we have in place a 'Clone' method to clone our object.

Concrete prototypes may then in turn look something like the following:

namespace PrototypePattern
{
   public class SportsCar : Car
   {
      public override Car Clone()
      {
         return this.MemberwiseClone() as Car;
      }

   }
}

You only need to implement the Clone operation to make this a prototypical instance, but the clone can have any manner of other options attached to it after the fact.

There is an inherent danger to be aware of, however, and that's the subject of how deep the copy goes.

Memberwise Clone will create what is known as a shallow copy.

This means that your first level objects such as the "TopSpeed," "EngineSize," and "Colour" properties shown above will be copied faithfully into an entirely separate and new instance of the object when it's cloned.

Any sub objects, however, will have their "Reference" cloned, but not an actual copy.

What this means in practice is that if you have any nested objects, which you then take a clone of as part of the parent object, a change to the property on the new clone will also update the same instance on the parent.

To make the clone work in a "Deep" fashion, you not only need to make a copy using a Memberwise clone, but you then need to "new" up a new copy of the sub object, then manually copy the values across, like so

      public override Car Clone()
      {
         var clone = this.MemberwiseClone() as Car;

         clone.embeddedObject = new EmbeddedObject();
         clone.embeddedObject.property =
            this.embeddedObject.property;

         return clone;
      }

Doing this ensures that you detach the embedded class from the copy attached to the master prototype, and then make sure it has the same values as previous.

In many cases, it makes more sense, especially under .NET, to use interfaces to create a prototypical instance. The pattern is closely related and in many ways a competitor to the "Abstract Factory" pattern. Generally, however when using the Prototype pattern you would manage object creation via a "Prototype Manager" that would keep close tracking on which patterns are used and where.

If you have a Design Pattern you'd like to see implemented in C#. drop me a comment below. Until next month, practice makes perfect.

Shawty

Share:
Home
Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved