© Vaskaran Sarcar 2020
V. SarcarDesign Patterns in C#https://doi.org/10.1007/978-1-4842-6062-3_2

2. Prototype Pattern

Vaskaran Sarcar1 
(1)
Garia, Kolkata, West Bengal, India
 

This chapter covers the Prototype pattern.

GoF Definition

Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.

Concept

The Prototype pattern provides an alternative method for instantiating new objects by copying or cloning an instance of an existing object. You can avoid the expense of creating a new instance using this concept. If you look at the intent of the pattern (the GoF definition), you see that the core idea of this pattern is to create an object that is based on another object. This existing object acts as a template for the new object.

When you write code for this pattern, in general, you see there is an abstract class or interface that plays the role of an abstract prototype. This abstract prototype contains a cloning method that is implemented by concrete prototypes. A client can create a new object by asking a prototype to clone itself. In the upcoming program (demonstration 1) of this chapter, I follow the same approach.

Real-World Example

Suppose that you have a master copy of a valuable document. You need to incorporate some changes to it to analyze the effect of the changes. In this case, you can make a photocopy of the original document and edit the changes in the photocopied document.

Computer-World Example

Let’s assume that you already have a stable application. In the future, you may want to modify the application with some small changes. You must start with a copy of your original application, make the changes, and then analyze it further. You do not want to start from scratch merely to make a change; this would cost you time and money.

In .NET, the ICloneable interface contains a Clone() method. In Visual Studio IDE, you can easily find the following details.
namespace System
{
    //
    // Summary:
    //     Supports cloning, which creates a new instance of a class with     //     the same value
 as an existing instance.
    [NullableContextAttribute(1)]
    public interface ICloneable
    {
        //
        // Summary:
        //     Creates a new object that is a copy of the current instance.
        //
        // Returns:
        //     A new object that is a copy of this instance.
        object Clone();
    }
}

You can use this built-in construct when you implement the Prototype pattern, but in this example, I used my own Clone() method.

Implementation

In this example, I follow the structure shown in Figure 2-1.
../images/463942_2_En_2_Chapter/463942_2_En_2_Fig1_HTML.jpg
Figure 2-1

Prototype example

Here BasicCar is the prototype. It is an abstract class that has an abstract method called Clone(). Nano and Ford are the concrete classes (i.e., concrete prototypes), which inherit from BasicCar. Both concrete classes have implemented the Clone() method . In this example, initially, I created a BasicCar object with a default price. Later, I modified that price per model. Program.cs is the client in the implementation.

Inside the BasicCar class, there is a method named SetAdditionalPrice() . It generates a random value between 200,000(inclusive) and 500,000(exclusive). This value is added to the base price before I calculate the final onRoad price of a car. In this example, I mention the price of these cars in Indian currency (Rupee).

A car model’s base price is set through the constructor of the concrete prototypes. So, you see the code segments like the following, where the concrete prototype (Nano) initializes the base price. Again, this class also overrides the Clone() method in BasicCar.
public class Nano : BasicCar
    {
        public Nano(string m)
        {
            ModelName = m;
            // Setting a basic price for Nano.
            basePrice = 100000;
        }
        public override BasicCar Clone()
        {
            // Creating a shallow copy and returning it.
            return this.MemberwiseClone() as Nano;
        }
    }

Ford, another concrete prototype, has a similar structure. In this example, I used two concrete prototypes (Ford and Nano). To better understand the Prototype pattern, one concrete prototype is enough. So, if you want, you can simply drop either of these concrete prototypes to reduce the code size.

Lastly and most importantly, you see the MemberwiseClone() method in the upcoming examples. It is defined in the Object class and has the following description.
// Summary:
//     Creates a shallow copy of the current System.Object.
//
// Returns:
//     A shallow copy of the current System.Object.
[NullableContextAttribute(1)]
protected Object MemberwiseClone();
Note

You may be wondering about the term shallow. Actually, there are two types of cloning: shallow and deep. This chapter includes a discussion and a complete program to help you understand their differences. For now, you only need to know that in a shallow copy, the simple type fields of a class are copied to the cloned instance; but for reference type fields, only the references are copied. So, in this type of cloning, both the original and cloned instances point to the same reference, which may cause problems in some cases. To overcome this, you may need to employ a deep copy.

Class Diagram

Figure 2-2 shows the class diagram.
../images/463942_2_En_2_Chapter/463942_2_En_2_Fig2_HTML.jpg
Figure 2-2

Class diagram

Solution Explorer View

Figure 2-3 shows the high-level structure of the parts of the program.
../images/463942_2_En_2_Chapter/463942_2_En_2_Fig3_HTML.jpg
Figure 2-3

Solution Explorer view

Demonstration 1

Here’s the implementation.
// BasicCar.cs
using System;
namespace PrototypePattern
{
    public abstract class BasicCar
    {
        public int basePrice = 0, onRoadPrice=0;
        public string ModelName { get; set; }
        /*
            We'll add this price before
            the final calculation of onRoadPrice.
        */
        public static int SetAdditionalPrice()
        {
            Random random = new Random();
            int additionalPrice = random.Next(200000, 500000);
            return additionalPrice;
        }
        public abstract BasicCar Clone();
    }
}
// Nano.cs
namespace PrototypePattern
{
    public class Nano : BasicCar
    {
        public Nano(string m)
        {
            ModelName = m;
            // Setting a base price for Nano.
            basePrice = 100000;
        }
        public override BasicCar Clone()
        {
            // Creating a shallow copy and returning it.
            return this.MemberwiseClone() as Nano;
        }
    }
}
// Ford.cs
namespace PrototypePattern
{
    public class Ford : BasicCar
    {
        public Ford(string m)
        {
            ModelName = m;
            // Setting a basic price for Ford.
            basePrice = 500000;
        }
        public override BasicCar Clone()
        {
            // Creating a shallow copy and returning it.
            return this.MemberwiseClone() as Ford;
        }
    }
}
// Client
using System;
namespace PrototypePattern
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***Prototype Pattern Demo*** ");
            // Base or Original Copy
            BasicCar nano = new Nano("Green Nano");
            BasicCar ford = new Ford("Ford Yellow");
            BasicCar basicCar;
            // Nano
            basicCar = nano.Clone();
            // Working on cloned copy
            basicCar.onRoadPrice = basicCar.basePrice + BasicCar.SetAdditionalPrice();
            Console.WriteLine($"Car is: {basicCar.ModelName}, and it's price is Rs. {basicCar.onRoadPrice}");
            // Ford
            basicCar = ford.Clone();
            // Working on cloned copy
            basicCar.onRoadPrice = basicCar.basePrice + BasicCar.SetAdditionalPrice();
            Console.WriteLine($"Car is: {basicCar.ModelName}, and it's price is Rs. {basicCar.onRoadPrice}");
            Console.ReadLine();
        }
    }
}

Output

The following is a possible output.
***Prototype Pattern Demo***
Car is: Green Nano, and it's price is Rs. 368104
Car is: Ford Yellow, and it's price is Rs. 878072
Note

You may see a different price in your system because I generated a random price in the SetAdditionalPrice() method inside the BasicCar class. But I ensured that the price of Ford is greater than Nano.

Modified Implementation

In demonstration 1, before making a clone, the client instantiated the objects as follows.
BasicCar nano = new Nano("Green Nano");
BasicCar ford = new Ford("Ford Yellow");

This is fine, but in some examples of the Prototype pattern, you may notice an additional participant creating the prototypes and supplying them to the client. Experts often like this approach because it hides the complexity of creating new instances from the client. Let’s look at how to implement this in demonstration 2.

Class Diagram

Figure 2-4 shows the key changes in the modified class diagram.
../images/463942_2_En_2_Chapter/463942_2_En_2_Fig4_HTML.jpg
Figure 2-4

Key changes in the class diagram for demonstration 2

Demonstration 2

To demonstrate this, I added the following class, called CarFactory , to our previous demonstration.
class CarFactory
    {
        private readonly BasicCar nano, ford;
        public CarFactory()
        {
            nano = new Nano("Green Nano");
            ford = new Ford("Ford Yellow");
        }
        public BasicCar GetNano()
        {
           return  nano.Clone();
        }
        public BasicCar GetFord()
        {
            return ford.Clone();
        }
    }
With this class, your client code can be modified as follows.
class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***Prototype Pattern Demo2.*** ");
            CarFactory carFactory = new CarFactory();
            // Get a Nano
            BasicCar basicCar = carFactory.GetNano();
            //Working on cloned copy
            basicCar.onRoadPrice = basicCar.basePrice + BasicCar.SetAdditionalPrice();
            Console.WriteLine($"Car is: {basicCar.ModelName}, and it's price is Rs. {basicCar.onRoadPrice}");
            // Get a Ford now
            basicCar = carFactory.GetFord();
            // Working on cloned copy
            basicCar.onRoadPrice = basicCar.basePrice + BasicCar.SetAdditionalPrice();
            Console.WriteLine($"Car is: {basicCar.ModelName}, and it's price is Rs. {basicCar.onRoadPrice}");
            Console.ReadLine();
        }
    }

Output

The following is a possible output.
***Prototype Pattern Demo2.***
Car is: Green Nano, and it's price is Rs. 546365
Car is: Ford Yellow, and it's price is Rs. 828518

Analysis

This output is just like the previous output, and there is no magic. The CarFactory class serves our needs, but there is a potential drawback to it. I initialized the cars inside the constructor of CarFactory. As a result, it always creates instances of both car types when the class is initialized. So, if you want to implement a lazy initialization, you can modify the GetNano() method in the CarFactory class , as follows.
public BasicCar GetNano()
        {
           if (nano!=null)
            {
                // Nano was created earlier.
                // Returning a clone of it.
                return nano.Clone();
            }
            else
            {
                /*
                  Create a nano for the first
                  time and return it.
                */
                nano = new Nano("Green Nano");
                return nano;
            }
        }

You can modify the GetFord() method in the same way.

Note

When you implement these changes, do not forget to remove the read-only modifier to avoid a compile-time error.

Here is the modified class.
class CarFactory
    {
        private BasicCar nano,ford;
        public BasicCar GetNano()
        {
           if (nano!=null)
            {
                // Nano was created earlier.
                // Returning a clone of it.
                return nano.Clone();
            }
            else
            {
                /*
                  Create a nano for the first
                  time and return it.
                */
                nano = new Nano("Green Nano");
                return nano;
            }
        }
        public BasicCar GetFord()
        {
           if (ford != null)
            {
                // Ford was created earlier.
                // Returning a clone of it.
                return ford.Clone();
            }
            else
            {
                /*
                  Create a nano for the first
                  time and return it.
                */
                ford = new Ford("Ford Yellow");
                return ford;
            }
        }
    }

Lastly, this is not the ultimate modification. In Chapter 1, you learned that in a multithreading environment, additional objects might be produced when you check the if-conditions. Since you learned possible solutions in Chapter 1, I do not focus on them in this discussion or upcoming discussions. I believe that you should now have a clear idea about the intent of this pattern.

Q&A Session

2.1 What are the advantages of using the prototype design pattern?

Here are some of the important usages.
  • You do not want to modify the existing object and experiment on that.

  • You can include or discard products at runtime.

  • In some contexts, you can create new instances at a cheaper cost.

  • You can focus on the key activities rather than focusing on complicated instance creation processes. For example, once you ignore the complex object creation processes, you can simply start with cloning or copying objects and implementing the remaining parts.

  • You want to get a feel for the new object’s behavior before you fully implement it.

2.2 What are the challenges associated with using the Prototype design pattern?

Here are some of the challenges.
  • Each subclass needs to implement the cloning or copying mechanism.

  • Implementing the cloning mechanism can be challenging if the objects under consideration do not support copying or if there are circular references.

In this example, I used the MemberwiseClone() member method, which provides a shallow copy. It is a very simple technique and can serve your basic needs. But if you need to provide a deep copy implementation for a complex object, it can be expensive because you not only need to copy the object, you also need to take care of all the references, which may form a very complicated graph.

2.3 Can you elaborate on the difference between a shallow copy and a deep copy in C#?

The following section explains their differences.

Shallow Copy vs. Deep Copy

A shallow copy creates a new object and then copies the nonstatic fields from the original object to the new object. If a value type field exists in the original object, a bit-by-bit copy is performed. But if the field is a reference type, this method copies the reference, not the actual object. Let’s try to understand the mechanism with a simple diagram (see Figure 2-5). Suppose that you have an object, X1, and it has a reference to another object, Y1. Further, assume that object Y1 has a reference to object Z1.
../images/463942_2_En_2_Chapter/463942_2_En_2_Fig5_HTML.jpg
Figure 2-5

Before the shallow copy of the references

With a shallow copy of X1, a new object (say, X2) is created that also has a reference to Y1 (see Figure 2-6).
../images/463942_2_En_2_Chapter/463942_2_En_2_Fig6_HTML.jpg
Figure 2-6

After a shallow copy of the reference

I used MemberwiseClone() in the implementation. It performs a shallow copy.

For a deep copy of X1, a new object (say, X3) is created, and X3 has a reference to the new object Y3 that is a copy of Y1. Also, Y3, in turn, has a reference to another new object, Z3, which is a copy of Z1 (see Figure 2-7).
../images/463942_2_En_2_Chapter/463942_2_En_2_Fig7_HTML.jpg
Figure 2-7

After a deep copy of the reference

Now consider the following demonstration to get a better understanding.

Demonstration 3

This simple demonstration shows you the difference between a shallow copy and a deep copy. It also shows you why a deep copy is important in certain situations. The following are the key characteristics of the program.
  • There are two classes: Employee and EmpAddress.

  • EmpAddress has only a single read-write property, called Address. It sets the address of an employee, but the Employee class has three read-write properties: Id, Name, and EmpAddress.

  • To form an Employee object, you need to pass an ID and the name of the employee, and at the same time, you need to pass the address. So, you see code segments like the following.
    EmpAddress initialAddress = new EmpAddress("21, abc Road, USA");
    Employee emp = new Employee(1, "John", initialAddress);
  • In the client code, first, you create an Employee object (emp), and then you create another object, empClone, through cloning. You see the following lines of code.
    Console.WriteLine("Making a clone of emp1 now.");
    Employee empClone = (Employee)emp.Clone();
  • Later, you change the values inside empClone.

When you use a shallow copy, a side effect of this change is that the address of the emp object also changed, which is unwanted. (The Prototype pattern is straightforward; you should not change the original object when you work on a cloned copy of the object).

In the following example, the code for the deep copy is initially commented so that you can see the effect of the shallow copy only.

Now go through the demonstration.
using System;
namespace ShallowVsDeepCopy
{
    class EmpAddress
    {
        public string Address { get; set; }
        public EmpAddress(string address)
        {
            this.Address = address;
        }
        public override string ToString()
        {
            return this.Address;
        }
        public object CloneAddress()
        {
            // Shallow Copy
            return this.MemberwiseClone();
        }
    }
    class Employee
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public EmpAddress EmpAddress { get; set; }
        public Employee(int id, string name, EmpAddress empAddress)
        {
            this.Id = id;
            this.Name = name;
            this.EmpAddress = empAddress;
        }
        public override string ToString()
        {
            return string.Format("Employee Id is : {0},Employee Name is : {1}, Employee Address is : {2}", this.Id,this.Name,this.EmpAddress);
        }
        public object Clone()
        {
            // Shallow Copy
            return this.MemberwiseClone();
            #region For deep copy
            //Employee employee = (Employee)this.MemberwiseClone();
            //employee.EmpAddress = (EmpAddress)this.EmpAddress.//CloneAddress();
            /*
             * NOTE:
             * Error: MemberwiseClone() is protected, you cannot access it via a qualifier of type EmpAddress. The qualifier must be Employee or its derived type.
             */
            //employee.EmpAddress = (EmpAddress)this.EmpAddress.MemberwiseClone(); // error
            // return employee;
            #endregion
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***Shallow vs Deep Copy Demo.*** ");
            EmpAddress initialAddress = new EmpAddress("21, abc Road, USA");
            Employee emp = new Employee(1, "John", initialAddress);
            Console.WriteLine("The original object is emp1 which is as follows:");
            Console.WriteLine(emp);
            Console.WriteLine("Making a clone of emp1 now.");
            Employee empClone = (Employee)emp.Clone();
            Console.WriteLine("empClone object is as follows:");
            Console.WriteLine(empClone);
            Console.WriteLine(" Now changing the name, id and address of the cloned object ");
            empClone.Id=10;
            empClone.Name="Sam";
            empClone.EmpAddress.Address= "221, xyz Road, Canada";
            Console.WriteLine("Now emp1 object is as follows:");
            Console.WriteLine(emp);
            Console.WriteLine("And emp1Clone object is as follows:");
            Console.WriteLine(empClone);
        }
    }
}

Output from a Shallow Copy

The following is the program’s output.
***Shallow vs Deep Copy Demo.***
The original object is emp1 which is as follows:
Employee Id is : 1,Employee Name is : John, Employee Address is : 21, abc Road, USA
Making a clone of emp1 now.
empClone object is as follows:
Employee Id is : 1,Employee Name is : John, Employee Address is : 21, abc Road, USA
 Now changing the name, id and address of the cloned object
Now emp1 object is as follows:
Employee Id is : 1,Employee Name is : John, Employee Address is : 221, xyz Road, Canada
And emp1Clone object is as follows:
Employee Id is : 10,Employee Name is : Sam, Employee Address is : 221, xyz Road, Canada

Analysis

There is an unwanted side effect. In the previous output, the address of the original object (emp) is modified due to modifying the cloned object (empClone). It happened because both the original object and the cloned object pointed to the same address, and they are not 100% disjointed. Figure 2-8 depicts the scenario.
../images/463942_2_En_2_Chapter/463942_2_En_2_Fig8_HTML.jpg
Figure 2-8

Shallow copy

Now let’s experiment with a deep copy implementation. Let’s modify the Clone method of the Employee class as follows. (I uncommented the code for the deep copy and commented out the code in the shallow copy.)
public Object Clone()
        {
            // Shallow Copy
            //return this.MemberwiseClone();
            #region For deep copy
            Employee employee = (Employee)this.MemberwiseClone();
            employee.EmpAddress = (EmpAddress)this.EmpAddress.CloneAddress();
            /*
             * NOTE:
             Error: MemberwiseClone() is protected, you cannot access it via a qualifier of type EmpAddress.The qualifier must be Employee or its derived type.
            */
            //employee.EmpAddress = (EmpAddress)this.EmpAddress.MemberwiseClone();//error
            return employee;
            #endregion
        }

Output from Deep Copy

Here is the modified output.
***Shallow vs Deep Copy Demo***
The original object is emp1 which is as follows:
Employee Id is : 1,Employee Name is : John, Employee Address is : 21, abc Road, USA
Making a clone of emp1 now.
empClone object is as follows:
Employee Id is : 1,Employee Name is : John, Employee Address is : 21, abc Road, USA
Now changing the name, id and address of the cloned object
Now emp1 object is as follows:
Employee Id is : 1,Employee Name is : John, Employee Address is : 21, abc Road, USA
And emp1Clone object is as follows:
Employee Id is : 10,Employee Name is : Sam, Employee Address is : 221, xyz Road, Canada

Analysis

This time, you do not see the unwanted side effect due to the modification to the empClone object. This is because the original object and cloned object are different from and independent of each other. Figure 2-9 depicts the scenario.
../images/463942_2_En_2_Chapter/463942_2_En_2_Fig9_HTML.jpg
Figure 2-9

Deep copy

Q&A Session

2.4 When should you choose a shallow copy over a deep copy (and vice versa)?

Here are the key reasons.
  • A shallow copy is faster and less expensive. It is always better to use if your target object has only the primitive fields.

  • A deep copy is expensive and slow, but it is useful if your target object contains many fields that have references to other objects.

2.5 In C#, if I need to copy an object, I need to use the MemberwiseClone() method . Is this correct?

No, there are alternatives available. For example, you can opt for a serialization mechanism when you implement a deep copy, or you can write your own copy constructor, and so forth. Each approach has its pros and cons. So, in the end, it is the developer’s call as to which approach best suits his needs. Many objects are very simple, and they do not contain references to other objects. So, to copy from those objects, a simple shallow copy mechanism is sufficient.

2.6 Can you show me an example that demonstrates the use of a copy constructor ?

Since C# does not support a default copy constructor, you may need to write your own copy constructor. Demonstration 4 is for your reference.

Demonstration 4

In this example, the Employee and EmpAddress classes both have almost the same description as in demonstration 3. The only difference is that this time, instead of the Clone() method in the Employee class, you notice the presence of a copy constructor inside it. Let’s proceed.

This time, using the following instance constructor,
// Instance Constructor
public Employee(int id, string name, EmpAddress empAddress)
{
        this.Id = id;
        this.Name = name;
        this.EmpAddress = empAddress;
}
you can create an object of Employee as follows.
EmpAddress initialAddress = new EmpAddress("21, abc Road, USA");
Employee emp = new Employee(1, "John",initialAddress);
In this Employee class, there is also a user-defined copy constructor , which is as follows.
// Copy Constructor
public Employee(Employee originalEmployee)
{
    this.Id = originalEmployee.Id;
    this.Name = originalEmployee.Name;
    //this.EmpAddress = (EmpAddress)this.EmpAddress.CloneAddress(); // ok
    this.EmpAddress = originalEmployee.EmpAddress.CloneAddress() as EmpAddress; // also ok
}
You can see that by using the copy constructor, I’m copying both the simple types (Id, Name) and the reference type (EmpAddress). So, once an Employee object like emp is created, you can create another empClone object from it using the following code.
Employee empClone= new Employee(emp);
As in the previous demonstration, once I created a copy (empClone) from the existing object (emp), I made changes to the copied object for verification purposes to make it easier to understand . Here is the complete code.
using System;
namespace UserdefinedCopyConstructorDemo
{
    class EmpAddress
    {
        public string Address { get; set; }
        public EmpAddress(string address)
        {
            this.Address = address;
        }
        public override string ToString()
        {
            return this.Address;
        }
        public object CloneAddress()
        {
            // Shallow Copy
            return this.MemberwiseClone();
        }
    }
    class Employee
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public EmpAddress EmpAddress { get; set; }
        // Instance Constructor
        public Employee(int id, string name, EmpAddress empAddress)
        {
            this.Id = id;
            this.Name = name;
            this.EmpAddress = empAddress;
        }
        // Copy Constructor
        public Employee(Employee originalEmployee)
        {
            this.Id = originalEmployee.Id;
            this.Name = originalEmployee.Name;
            //this.EmpAddress = (EmpAddress)this.EmpAddress.CloneAddress(); // ok
            this.EmpAddress = originalEmployee.EmpAddress.CloneAddress() as EmpAddress; // Also ok
        }
        public override string ToString()
        {
            return string.Format("Employee Id is : {0},Employee Name is : {1}, Employee Address is : {2}", this.Id, this.Name, this.EmpAddress);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***A simple copy constructor demo*** ");
            EmpAddress initialAddress = new EmpAddress("21, abc Road, USA");
            Employee emp = new Employee(1, "John",initialAddress);
            Console.WriteLine("The details of emp is as follows:");
            Console.WriteLine(emp);
            Console.WriteLine(" Copying from emp1 to empClone now.");
            Employee empClone= new Employee(emp);
            Console.WriteLine("The details of empClone is as follows:");
            Console.WriteLine(empClone);
            Console.WriteLine(" Now changing the id,name and address of empClone.");
            empClone.Name = "Sam";
            empClone.Id = 2;
            empClone.EmpAddress.Address= "221, xyz Road, Canada";
            Console.WriteLine("The details of emp is as follows:");
            Console.WriteLine(emp);
            Console.WriteLine("The details of empClone is as follows:");
            Console.WriteLine(empClone);
            Console.ReadKey();
        }
    }
}

Output

Here is the sample output .
***A simple copy constructor demo***
The details of emp is as follows:
Employee Id is : 1,Employee Name is : John, Employee Address is : 21, abc Road, USA
 Copying from emp1 to empClone now.
The details of empClone is as follows:
Employee Id is : 1,Employee Name is : John, Employee Address is : 21, abc Road, USA
Now changing the id,name and address of empClone.
The details of emp is as follows:
Employee Id is : 1,Employee Name is : John, Employee Address is : 21, abc Road, USA
The details of empClone is as follows:
Employee Id is : 2,Employee Name is : Sam, Employee Address is : 221, xyz Road, Canada

Analysis

Note the final portion of the output. It reflects that the changes were properly made to the copied object only.

This chapter showed you multiple implementations of prototype design patterns and discussed the difference between a shallow copy and a deep copy. You also learned about a user-defined copy constructor. Now you can move to the next chapter and learn about the Builder pattern.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.5.68