Prototype design pattern is one of the simplest design pattern. I think of all the design patterns, Prototype is simpler to implement and understand just like Singleton. Lets continue with the Prototype pattern in this post which extends the earlier Design Patterns Series.
Problem Statement
Assume there is a supermarket chain who have their business setup in different parts of a country. When a new store opens up they setup the infrastructure for the store. There are some predefined types of stores like the convenient stores which we can call Express stores. Then there are bigger stores which we can call Super stores. There can be multiple attributes associated with the stores. Each store can have one or more floors for customers to shop. Each store should have at least one checkout counter. The store can be open for customers all throughout the day. We can keep on adding many more attributes like this. But for simplicity we will stop here. Lets see how we can create these types of stores.
Solution Without Prototype Pattern
Lets start by creating a class which can be used to create these different types of stores.We call this class StoreCreator. The implementation of the class is very straightforward.
public class StoreCreator
{
public Store CreateExpressStore()
{
return new Store
{
NumberOfFloors = 1,
NumberOfCheckoutCounters = 1,
OpenAllDay = false
};
}
public Store CreateSuperStore()
{
return new Store
{
NumberOfFloors = 2,
NumberOfCheckoutCounters = 10,
OpenAllDay = true
};
}
}
Based on the above code snippets, we can create two distinct types of stores. An express store which has 1 floor, 1 checkout and is not open for the whole day. Similarly we can create a super store with 2 floors, 10 checkouts and is open for the complete day. The store class is a simple class with just properties.
Now lets look at the client code which uses this class.
internal class StoreCreatorWithoutPrototype
{
public StoreCreatorWithoutPrototype()
{
StoreCreator storeCreator = new StoreCreator();
Store bangaloreExpressStore = storeCreator.CreateExpressStore();
bangaloreExpressStore.Address = "Bangalore";
Store mumbaiExpressStore = storeCreator.CreateExpressStore();
mumbaiExpressStore.Address = "Mumbai";
Store bangaloreSuperStore = storeCreator.CreateSuperStore();
bangaloreSuperStore.Address = "Bangalore";
Store delhiSuperStore = storeCreator.CreateSuperStore();
delhiSuperStore.Address = "Delhi";
delhiSuperStore.NumberOfCheckoutCounters = 8;
delhiSuperStore.OpenAllDay = false;
delhiSuperStore.NumberOfFloors = 4;
Store chennaiSuperStore = storeCreator.CreateSuperStore();
chennaiSuperStore.Address = "Chennai";
chennaiSuperStore.NumberOfCheckoutCounters = 8;
chennaiSuperStore.OpenAllDay = false;
chennaiSuperStore.NumberOfFloors = 4;
}
}
We have created 5 different stores using the above code. First one is the bangaloreExpressStore. Second is the mumbaiExpressStore. Third is the bangaloreSuperStore. For all these three stores we are using the default settings from the respective create methods.
In real life we will always have exceptions or customizations to make. So the next case is a delhiSuperStore which has 8 checkout counters instead of 10. It is not open all day. And it has 4 floors instead of 2.
Next is the chennaiSuperStore which has exactly the same attributes as the delhi super store created previously. We have duplicated most of the code above. Duplicate code is very bad code smell which we should try to avoid at any cost.
Problem with this approach
Apart form the code duplication, the above code can cause other problems. In this case we have only three or four properties, imagine what will happen if we have more than 10-15 properties. Assume only 2-3 properties are same as the default ones and the remaining ones are customized. Lets assume in the above example the delhiSuperStore had customized 12 properties out of 15. Now we need to create the chennaiSuperStore which is exactly same as delhiSuperStore except for the address.
This is a quite common scenario. We have certain object which is initialized with the values. During the course of the execution of the program, we need another object with most of the values same as the already existing object. The cost of creating the object and initializing it can be very high in some scenarios. In our case we are setting the values in create method. Imagine the scenario where a complex object is constructed using different components or services. In this case the cost of constructing another object with similar attributes can be avoided if we have some mechanism of using the already constructed objects values.
This is the objective of the Prototype pattern. It allows us to create a new object by using a fully initialized object which is called the prototype. This is similar to having a model flat when you go to buy a flat in an apartment under construction. Model flat will have most of the common attributes and then build or the developer might give you a choice to customize few things here a and there as per your needs.
Refactored Solution With Prototype Pattern
Lets see how we can refactor our code to fit this criteria. If we look at the classes that we have in our solution, the Store class is the one which we are initializing. Once we have an instance of store class we can make use of it to clone it into another instance. Being the good students of Object Oriented design, we can define an interface which can help us do the cloning part. Since we are going to clone or prototype a store, we can name our interface as IStorePrototype
public interface IStorePrototype
{
Store Clone();
}
It has only one method Clone which return an instance of Store. Lets look at the class which implements this interface. Any guesses?
public class Store : IStorePrototype
{
public bool OpenAllDay { get; set; }
public string Address { get; set; }
public int NumberOfCheckoutCounters { get; set; }
public int NumberOfFloors { get; set; }
public Store Clone()
{
return new Store
{
NumberOfCheckoutCounters = this.NumberOfCheckoutCounters,
NumberOfFloors = this.NumberOfFloors,
OpenAllDay = this.OpenAllDay
};
}
}
The Store class implements the IStorePrototype interface. The Clone method copies the values for existing properties into the new instance. Lets see the changes on the client who uses this refactored code.
internal class StoreCreatorWithPrototype
{
public StoreCreatorWithPrototype()
{
StoreCreator storeCreator = new StoreCreator();
Store bangaloreExpressStore = storeCreator.CreateExpressStore();
bangaloreExpressStore.Address = "Bangalore";
Store mumbaiExpressStore = storeCreator.CreateExpressStore();
mumbaiExpressStore.Address = "Mumbai";
Store bangaloreSuperStore = storeCreator.CreateSuperStore();
bangaloreSuperStore.Address = "Bangalore";
Store delhiSuperStore = storeCreator.CreateSuperStore();
delhiSuperStore.Address = "Delhi";
delhiSuperStore.NumberOfCheckoutCounters = 8;
delhiSuperStore.OpenAllDay = false;
delhiSuperStore.NumberOfFloors = 4;
Store chennaiSuperStore = delhiSuperStore.Clone();
chennaiSuperStore.Address = "Chennai";
}
}
There is no change in the way we create the first 4 store instances. But look at the last one. Since the chennaiSuperStore needs to have exact same attributes as delhiSuperStore, we just Clone the delhiSuperStore and assign it to chennaiSuperStore. By doing this we get the exact state of the delhiSuperStore at runtime into the chennaiSuperStore instance.
Assume that the retail chain is planning a big expansion and wants to replicate the model to various stores. Using this method mentioned in this post, we can create one or more prototypes or model stores and then easily expand them into the required number of instances.
Conclusion
Prototype pattern is very useful when we want to replicate the state of an initialized object into another instance of the same class. Prototype is a creational pattern and deals with creating instances of a class. People might argue that the same thing can be achieved using a Factory method which takes multiple parameters to create a new instance.
Once again while applying a design pattern, we need to look at its intent and the application of the pattern. The intent of Factory is to create one of the several types of objects of a family of classes. Whereas Prototype pattern is used to initialize a object based on another object which is fully initialized. This is helpful when the creation of an object might be complex compared to cloning it from an existing object.
Lets say the supermarket chain has operations in different countries. We can have a prototype for each county and all the stores within that country can have attributes defined for the country prototype store. This can help easily extend the supermarket chain’s business when it decides to add another country to its operations. It would be as simple as creating a new prototype for the specific country and they can start rolling the stores.
As always the complete working solution is available for download Prototype Design Pattern Demo.zip.
Until next time Happy Programming.
Further Reading
Here are some books I recommend related to the topics discussed in this post.
I think your blog could use more of these ADBRITE ads. Is there a way to make them LOUDER?
ReplyDeleteGood and nice ready reckoner
ReplyDeleteGood read and a nice ready reckoner..
ReplyDeleteThanks for the compliments Dwarak
ReplyDelete