Definition
This design pattern is all about object copying, the use case would be when its easier to copy (clone) an existing object than to fully initialize a new one. Note that the existing object could be partially or fully constructed.
These would be complicated objects, perhaps constructed with a builder and you now wish to make simple changes to it for your use case.
A Deep Copy is what is required for the Prototype Pattern.
Copy | Description |
---|---|
Shallow | A shallow copy is a copy of the object and its references. This means after you make the copy changes to this object also makes changes to the other object. |
Deep | A deep copy is not just a copy of the object but a copy of all its references (on the heap) by making new objects which replicates the state of those references. This would need to be done recursively so you would need to make a complete copy of the object. This means the change in this object does not affect the other object. |
Caveats
- When deep copying an entity, if it has a database auto assigned ID you may want to reset this as should the copied entity change and then be persisted you may update persisted data by mistake.
ICloneable
Don’t use ICloneable for the Prototype Pattern as it will create a shallow copy.
- [PatternsAndPrinciples/PrototypePattern/ICloneableDemo.cs](https://github.com/carlpaton/Boilerplate/blob/master/.Net Core Console Application/PatternsAndPrinciples/PrototypePattern/ICloneableDemo.cs)
Copy Constructors
Don’t use Copy Constructors as this would not be idiomatic for a C#
developer and requires each custom type to have a copy constructor to allow traversing of the tree.
- [PatternsAndPrinciples/PrototypePattern/CopyConstructorDemo.cs](https://github.com/carlpaton/Boilerplate/blob/master/.Net Core Console Application/PatternsAndPrinciples/PrototypePattern/CopyConstructorDemo.cs)
Explicit Deep Copy Interface
You could create your own Explicit Deep Copy Interface like IProtoType<T>
however this would require manual object traversing and having each dependent object in the tree implement IProtoType<T>
.
- [PatternsAndPrinciples/PrototypePattern/IProtoTypeDemo.cs](https://github.com/carlpaton/Boilerplate/blob/master/.Net Core Console Application/PatternsAndPrinciples/PrototypePattern/IProtoTypeDemo.cs)
Copy Through Serialization (Binary)
- [PatternsAndPrinciples/PrototypePattern/BinarySerializationDemo.cs](https://github.com/carlpaton/Boilerplate/blob/master/.Net Core Console Application/PatternsAndPrinciples/PrototypePattern/BinarySerializationDemo.cs)
Serialization will be slower but will traverse the entire object tree. As we will not be persisting the serialized object to storage it can simply be kept in a MemoryStream
.
Using a BinaryFormatter
makes the most sense as we don’t need human readable data (XML, JSON).
Create the extension method:
1 | public static class ExtensionMethods |
Create some classes to hold the state to be copied, caveat is the classes need to have the [Serializable]
attribute / annotation.
1 | [ ] |
Instantiate and copy
1 | var carl = new Person("Carl", new Address("Sale Street", 66)); |
Output
1 | FirstName: Carl, Address: StreetName: Sale Street, HouseNumber: 66 |
Copy Through Serialization (XML)
- [PatternsAndPrinciples/PrototypePattern/XmlSerializationDemo.cs](https://github.com/carlpaton/Boilerplate/blob/master/.Net Core Console Application/PatternsAndPrinciples/PrototypePattern/XmlSerializationDemo.cs)
The classes you wish to serialize may not be open for modification, so you could use XMlSerializer
if you cannot add the annotation. Caveat would however be that the classes need a parameterless constructor.
The extension method would then look like this
1 | public static class ExtensionMethods |
Copy Through Serialization (System.Text.Json)
- [PatternsAndPrinciples/PrototypePattern/JsonSerializationDemo.cs](https://github.com/carlpaton/Boilerplate/blob/master/.Net Core Console Application/PatternsAndPrinciples/PrototypePattern/JsonSerializationDemo.cs)
- https://www.nuget.org/packages/System.Text.Json/
Some Caveats still exist:
- Parameterless constructor is needed
- This serializer doesn’t work with fields but needs properties instead.
1 | public class Person |
The extension method would look like this:
1 | public static class ExtensionMethods |
Copy Through Serialization (Newtonsoft.Json)
- [PatternsAndPrinciples/PrototypePattern/JsonSerializationDemo2.cs](https://github.com/carlpaton/Boilerplate/blob/master/.Net Core Console Application/PatternsAndPrinciples/PrototypePattern/JsonSerializationDemo2.cs)
- https://www.nuget.org/packages/Newtonsoft.Json/
No additional changes are needed to the class that I could see. The extension method would look like this:
1 | public static class ExtensionMethods |
References
- https://docs.microsoft.com/en-us/dotnet/api/system.object.memberwiseclone?view=netcore-3.1
- https://docs.microsoft.com/en-us/dotnet/api/system.icloneable?view=netcore-3.1
- https://www.udemy.com/course/design-patterns-csharp-dotnet/
- [https://github.com/carlpaton/Boilerplate/tree/master/.Net%20Core%20Console%20Application/PatternsAndPrinciples/PrototypePattern](https://github.com/carlpaton/Boilerplate/tree/master/.Net Core Console Application/PatternsAndPrinciples/PrototypePattern)