Prototype Pattern in C#
Tags: beginners, tutorial, programming, csharp
Published: 2023-04-13 06:53:14

The **Prototype** is a creational design pattern that lets us copy existing objects without making our code dependent on our classes.

The typical way of thinking about this pattern is to consider how we might model the colour spectrum.  There are something like [10 million visible colours](https://en.wikipedia.org/wiki/Color), so modelling them as individual classes (e.g. `Red`, `LightMauve`, `Skobeloff`, `Macaroni and Cheese`, `Zomp`) would be rather impractical.

However, a colour is a colour, no matter what colour it is; colours have the same properties as each other even though they don't have the same values.  If we needed to create a lot of colour instances, we could do so by using the Prototype design pattern.

## Conceptualizing the Problem

Say we have an object we want to create an exact copy of. How would we do it? First, we have to create a new object of the same class. Then we have to go through all the fields of the original instance and copy their values to the new one.

Excellent. But there's a catch. Not all objects can be copied that way because some of the object's fields may be private and not visible from outside of the object itself.

There's one more problem with the direct approach. Since we have to know the object's class to create a duplicate, our code becomes dependent on that class. If the extra dependency doesn't scare you there's another catch. Often, we only know the interface the object implements, but not its concrete class.

The Prototype pattern delegates the cloning process to the objects that are being cloned. The pattern declares a common interface for all objects that support cloning. This interface lets us clone an instance without coupling our code with its class. More often than not, such an interface contains only a single `Clone` method.

The implementation of the `Clone` method is very similar in all classes. The method creates a new instance of the current class. Then it carries over all of the values of the old instance into the new one.

An object that supports cloning is called a *prototype*. When our objects have dozens of fields and hundreds of possible configurations, cloning them might serve as an an alternative to subclassing. Here's how it works: We create a set of objects, configured in various ways. When we need an object configured in a specific way, we just clone a prototype instead of constructing a new instance from scratch.

## Structuring the Prototype 

The prototype pattern has two popular flavors: The **Basic Prototype** implementation, and the **Prototype Registry** implementation:

### Basic Prototype Implementation

In its base implementation, the **Prototype** pattern has 3 participants:
- **Prototype**: The **Prototype** interface declares the cloning methods. In most cases, it's a single `Clone` method, but  it can be more complicated depending on the context.
- **Concrete Prototype**: The cloning method is implemented by the **Concrete Prototype** class. This method may handle several edge scenarios of the cloning process such as cloning linked objects, untangling recursive dependencies, and so on, in addition to transferring the original object's contents to the clone.
- **Client**: The **Client** can produce a copy of any object that implements the prototype interface.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/41zocr7jy50vmut65i1k.PNG)

### Prototype Registry Implementation

In the base implementation, the **Prototype Registry** pattern has 4 participants:
- **Prototype**: The **Prototype** interface declares the cloning methods. In most cases, it's a single `Clone` method, but  it can be more complicated depending on the context.
- **Concrete Prototype**: The cloning method is implemented by the **Concrete Prototype** class. This method may handle several edge scenarios of the cloning process such as cloning linked objects, untangling recursive dependencies, and so on, in addition to transferring the original object's contents to the clone.
- **Prototype Registry**: The **Prototype Registry** makes it simple to find regularly used prototypes. It keeps a collection of ready-to-copy pre-built items. A name prototype hash map is the most basic prototype registry. If you require more search parameters than just a name, you may create a far more sophisticated version of the registry.
- **Client**: The **Client** can produce a copy of any object that implements the prototype interface.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0nmrtaxrmd4fabnx09e6.PNG)

To demonstrate how the Prototype pattern works we are going to create a system that generates different shapes.

First, we'll create an `AbstractShape` class (the **Prototype** participant) to represent a shape, and define a method `Clone()` which an instance of this class can use to clone itself:

```csharp
/// <summary>  
/// The Prototype abstract class  
/// </summary>  
public abstract class Shape  
{  
    public int x;  
    public int y;  
    public string color;  
  
    public Shape()  
    {  
    }  
  
    public Shape(Shape target)  
    {  
        if (target != null)  
        {  
            this.x = target.x;  
            this.y = target.y;  
            this.color = target.color;  
        }  
    }  
  
    public abstract Shape clone();  
  
    public override bool Equals(object object2)  
    {  
        if (!(object2 is Shape)) return false;  
        Shape shape2 = (Shape)object2;  
        return shape2.x == x && shape2.y == y && object.Equals(shape2.color, color);  
    }  
}
```

Now that we have our abstract class, we will implement our **Concrete Prototypes**. The first concrete prototype will create a circle. It will handle cloning and equality comparison.

```csharp
public class Circle : Shape  
{  
    public int Radius;  
  
    public Circle()  
    {  
    }  
  
    public Circle(Circle target) : base(target)  
    {  
        if (target != null)  
        {  
            this.Radius = target.Radius;  
        }  
    }  
  
    public override Shape clone()  
    {  
        return new Circle(this);  
    }  
  
    public override bool Equals(object object2)  
    {  
        if (!(object2 is Circle shape2) || !base.Equals(shape2)) return false;  
        return shape2.Radius == Radius;  
    }  
  
    public override int GetHashCode()  
    {  
        var hashCode = base.GetHashCode();  
        hashCode = hashCode * 31 + Radius.GetHashCode();  
        return hashCode;  
    }  
}
```
The second concrete prototype will create a rectangle (oh how original!). Again, it will handle cloning and equality comparisons.

```csharp
public class Rectangle : Shape  
{  
    public int Width;  
    public int Height;  
  
    public Rectangle()  
    {  
    }  
  
    public Rectangle(Rectangle target) : base(target)  
    {  
        if (target != null)  
        {  
            this.Width = target.Width;  
            this.Height = target.Height;  
        }  
    }  
  
    public override Shape clone()  
    {  
        return new Rectangle(this);  
    }  
  
    public override bool Equals(object object2)  
    {  
        if (!(object2 is Rectangle) || !base.Equals(object2)) return false;  
        Rectangle shape2 = (Rectangle)object2;  
        return shape2.Width == Width && shape2.Height == Height;  
    }  
  
    public override int GetHashCode()  
    {  
        int hashCode = base.GetHashCode();  
        hashCode = hashCode * 31 + Width.GetHashCode();  
        hashCode = hashCode * 31 + Height.GetHashCode();  
        return hashCode;  
    }  
}
```

Now we need to create a bunch of shapes.

In our `Main()` method (which does double-duty as our **Client** participant), we can do just that, by instantiating the prototype and then cloning it:

```csharp
public static void Main(string[] args)  
{  
    List<Shape> shapes = new List<Shape>();  
    List<Shape> shapesCopy = new List<Shape>();  
  
    Circle circle = new Circle();  
    circle.x = 10;  
    circle.y = 20;  
    circle.Radius = 15;  
    circle.color = "red";  
    shapes.Add(circle);  
  
    Circle anotherCircle = (Circle)circle.clone();  
    shapes.Add(anotherCircle);  
  
    Rectangle rectangle = new Rectangle();  
    rectangle.Width = 10;  
    rectangle.Height = 20;  
    rectangle.color = "blue";  
    shapes.Add(rectangle);  
  
    cloneAndCompare(shapes, shapesCopy);  
}  
  
private static void cloneAndCompare(List<Shape> shapes, List<Shape> shapesCopy)  
{  
    shapesCopy.AddRange(shapes.Select(shape => shape.clone()));  
  
    for (var i = 0; i < shapes.Count; i++)  
    {  
        if (shapes[i] != shapesCopy[i])  
        {  
            Console.WriteLine(i + ": Shapes are different objects (Exquisite!)");  
            if (shapes[i].Equals(shapesCopy[i]))  
            {  
                Console.WriteLine(i + ": And they are identical (Charming!)");  
            }  
            else  
            {  
                Console.WriteLine(i + ": But they are not identical (Obscene!)");  
            }  
        }  
        else  
        {  
            Console.WriteLine(i + ": Shape objects are the same (Indecorous!)");  
        }  
    }  
}
```

If we run our program we will get the following:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6wo4gd7xqsruxzj389fe.PNG)

### Prototype Registry
We may put up a centralized prototype registry (or factory) with a library of pre-defined prototype objects. We could then obtain new objects from the factory by giving their names or other parameters. The manufacturer would look for an adequate prototype, clone it, and send us a duplicate.

```csharp
using PrototypeRegistry.Registry;  
using PrototypeRegistry.Shapes;  
  
public class Program {  
    public static void Main(string[] args)  
    {  
        BundledShapeRegistry cache = new BundledShapeRegistry();  
  
        Shape shape1 = cache.Get("Big green circle");  
        Shape shape2 = cache.Get("Medium blue rectangle");  
        Shape shape3 = cache.Get("Medium blue rectangle");  
  
        if (shape1 != shape2 && !shape1.Equals(shape2))  
        {  
            Console.WriteLine("Big green circle != Medium blue rectangle (yay!)");  
        }  
        else  
        {  
            Console.WriteLine("Big green circle == Medium blue rectangle (booo!)");  
        }  
  
        if (shape2 != shape3)  
        {  
            Console.WriteLine("Medium blue rectangles are two different objects (yay!)");  
            if (shape2.Equals(shape3))  
            {  
                Console.WriteLine("And they are identical (yay!)");  
            }  
            else  
            {  
                Console.WriteLine("But they are not identical (booo!)");  
            }  
        }  
        else  
        {  
            Console.WriteLine("Rectangle objects are the same (booo!)");  
        }  
    }  
}
```

If we run our program we will get the following:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gf1fed59fqbk87lmn4cv.PNG)

## Pros and Cons of the Prototype Pattern
|||
|:--|:--|
|✔We can clone objects without coupling to their concrete classes.|❌Cloning complex objects that have circular references might be very tricky.|
|✔ We can get rid of repeated initialization code in favor of cloning pre-built prototypes.||
|✔ We can produce complex objects more conveniently.||
|✔We get an alternative to inheritance when dealing with configuration presets for complex objects.||

## Relations to Other Patterns
- Many designs begin with the **Factory Method** (which is less difficult and more adjustable through subclasses) and progress to the **Abstract Factory**, **Prototype**, or **Builder** (which is more versatile but more complicated).
- **Abstract Factory** classes are frequently built on a collection of **Factory Methods**, however, the methods in these classes can also be composed using **Prototype**.
- When you need to preserve copies of **Commands** in history, **Prototype** might be helpful.
- Designs that heavily rely on **Composite** and **Decorator** can frequently benefit from the use of **Prototypes**. You can clone complicated structures using the pattern rather than rebuilding them from scratch.
- Because the **Prototype** is not dependent on inheritance, it does not have any of its disadvantages. **Prototype**, on the other hand, necessitates a complex initialization of the copied object. The **Factory Method** is an inheritance-based method that does not require an initialization step.
- **Prototype** is sometimes a simpler alternative to **Memento**. This works if the object whose state you wish to save in history is simple and lacks ties to other resources, or if the linkages are simple to re-establish.
- **Singletons** may be used to implement **Abstract Factories**, **Builders**, and **Prototypes**.

## Final Thoughts
In this article, we have discussed what is the Prototype pattern, when to use it and what are the pros and cons of using this design pattern. We then examined some use cases for this pattern and how the Prototype relates to other classic design patterns. 

It's worth noting that the Prototype pattern, along with the rest of the design patterns presented by the Gang of Four, is not a panacea or a be-all-end-all solution when designing an application. Once again it's up to the engineers to consider when to use a specific pattern. After all these patterns are useful when used as a precision tool, not a sledgehammer.