Jump to content

Prototype

From Knowledge Base

Prototype

Error creating thumbnail: Unable to save thumbnail to destination

The Prototype Pattern (Creational) allows you to create new objects by copying an existing object (prototype). This can be useful when you want to create new instances that are similar to existing ones without the overhead of initializing them from the database or other data sources.

Benefits

  • Performance: It can be more efficient when creating objects from a prototype is cheaper than creating them anew.
  • Avoids Subclassing: Instead of relying on inheritance, objects can be configured at runtime by cloning a prototype.
  • Flexibility: New object instances can be created at runtime with values copied from a prototype, allowing for dynamic behavior.
  • Specifying New Objects: By cloning a prototype, you can specify new objects by varying values from the prototype.

Use Cases

The Prototype Pattern is often used in scenarios where systems need to decouple the creation of objects from the classes that implement them. Common use cases include:

  • Configured Systems: When objects need to be created that match a certain configuration, a prototype can be cloned and altered as needed.
  • Avoiding Expensive Creation Steps: In cases where the creation of an object involves costly operations like database queries or complex configuration, a prototype can be cloned to bypass these steps.
  • Dynamic Loading: The pattern allows for dynamic loading and adding of new classes at runtime.

How It Works

The Prototype Pattern typically involves two core components:

  • Prototype: This is the interface that defines the cloning method.
  • Concrete Prototype: The class that implements the Prototype interface and defines the method to clone itself.

Implementation Example

Prototype interface

public interface IPrototype<T> where T : class {
    T Clone();
}

Prototype class

public class Data : IPrototype<Data> {
    public int Id { get; set; }
    public string Name { get; set; }
    
    // Other properties...

    // Implementation of the Clone method from IPrototype
    public Data Clone() {
        // Shallow copy: the new object will have the same value-type fields.
        // If there are any reference-type fields, they will point to the same objects.
        return (Data)this.MemberwiseClone();

        // For a deep copy (if needed), you would need to manually copy the reference-type fields
        // so that the clone references new instances rather than the original instances.
    }
}

A prototype factory that will be responsible for cloning the Data objects:

public class DataPrototypeFactory {
    private Data _prototype;

    public DataPrototypeFactory(Data prototype) {
        _prototype = prototype;
    }

    public Data CreateCopy() {
        // Use the Clone method to create a copy of the prototype
        return _prototype.Clone();
    }
}

`AsNoTracking()` is used to retrieve an entity without tracking it in the DbContext to ensure that changes to the copied entity won't affect the original.

Main Method

// Assume you have a prototype instance of Data
var prototypeData = new Data
{
    Id = 1,
    Name = "Prototype"
};

// Create a factory with the prototype
var factory = new DataPrototypeFactory(prototypeData);

// Create a copy of the prototype
var copiedData = factory.CreateCopy();

// Modify the copied data
copiedData.Name = "Copied";

// Output the names to verify that they are indeed separate instances
Console.WriteLine($"Original Data Name: {prototypeData.Name}"); // Output: Prototype
Console.WriteLine($"Copied Data Name: {copiedData.Name}"); // Output: Copied

Practice in C#

The Prototype pattern is particularly useful in scenarios where objects are costly to create or require a complex initialization process:

  • Cloning Complex Objects: When you have an instance of a complex object that has already been configured, and you need to create a duplicate with the same state or with minor changes. This is common in graphic editors or scenarios where the object's initialization process is resource-intensive, and you want to avoid repeating the process.
  • Resetting State: In applications where you need to frequently reset an object to a known state, such as in gaming or simulation applications. Instead of re-initializing the object from scratch or keeping track of the initial state to restore it, you can clone a prototype object that was saved in the desired initial state.