Next pattern that I want to discuss is the Prototype pattern from the Gang of Four Design Patterns. For a detailed introduction to this pattern please see here or refer to the Gang of Four Design Patterns book.
The prototype pattern is an interesting little pattern (little in the sense that it's not that complicated) which has been mostly superseded by language mechanisms available in many modern programming languages. But it still remains as a nice topic to discuss and learn since the more proficient you are at this pattern the better you can use those language specific features (which will be discussed later) and understand where they might not work out for your design and you'd have to implement the prototype pattern from scratch. As always my discussions here are going to be based on the C# language and .net features but Java programmers will also find a whole lot of features that are completely similar and will benefit from this discussion.
The prototype pattern is another pattern in the list of creational patterns. Creational patterns by definition provide a mechanism to create new objects in ways more flexible than simply using the 'new' keyword. The two patterns discussed so far Singleton & Abstract Factory are also creational patterns and they serve the same exact goal. For example when using the Abstract Factory pattern you don't use the 'new' keyword to create a product but you call the abstract factory's CreateXYZProduct() method that will create the specific product and return an interface to it. This level of indirection (calling a method that will create the object on your behalf) is how the pattern is providing the added flexibility and the ability to swap families of products without touching the client code. The prototype pattern is also considered a creational pattern because it provides the same concept. It gives you a more flexible way of creating an object.
Let's discuss the pattern by examining an example. Suppose you are building a graphic design tool in which the user gets a canvas and can pick from different drawing shapes (such as circle, rectangle, line …) and use them to design some graphics on the canvas. Now let's focus on the toolbox of shapes that we are providing for the user: Suppose one of our requirements state that alongside the x number of shapes that we are supporting built-in we are also supposed to support the ability to add new shapes in the future (without changing or re-compiling the currently deployed software). In this case the routines that are creating each shape need an added level of flexibility to be able to add any shape, even the ones that are added in the future (by someone else). This is a perfect example of where the prototype pattern can be used. But before we get into the pattern details let's address another issue with the above mentioned requirements: If a shape is created in the future how am I supposed to know how to use it: obviously to the experienced OO designer this is a trivial question since all you need to do is look at the shapes from a level of abstraction that is common between all shapes. In other words I'm going to assume that we will have a Shape class or an IShape interface that all of our shapes (even the ones built in the future) will inherit from (implement) and therefore we can talk to all of them through that common abstract interface.
OK back to our pattern discussion:
The prototype patterns general structure looks something like this:
Before getting into any discussion about the pattern let's apply the above structure to our example:
As can be seen the prototypes are our shapes (or toolbox items), they provide the functionality that we expect from each shape (i.e. to appear in the toolbox, to be drawn on the canvas, resize, etc.), but they also provide an extra piece of functionality: the ability to clone themselves and provide an exact replica of this object. Now this ability might look trivial when we know what the object's type is but when you look at this ability from an abstract level (for example the Prototype level or in this case the Shape level), it seems rather useful. What we have in the ShapeFactory class (Prototype factory) is a list of all possible shapes (prototypes). Whenever a shape is selected by the user, we calculate that shapes index based on where the user clicked on our toolbox and then ask the ShapeFactory class to provide us with that shape (the Create method). The ShapeFactory in turn finds that shape (prototype) in the list of shapes that it contains and asks it to create an exact instance of itself. Now this shape could be one of the built-in shapes or a shape added later by another programmer. The above example perfectly shows that the Clone method although trivial at the specific object type level, is extremely powerful at the abstract level and of great use to the prototype factory class.
We've had an assumption so far that must be addressed: we had assumed that the prototype factory class will somehow have a list of prototype objects. With those prototype objects it can clone them and create new ones but the question is where did it get the prototype objects in the first place. It's similar to the egg & the chicken problem: which one came first?
The context in which you use the prototype pattern greatly affects how the prototype factory will obtain the list of prototypes. A very simple example is situations where (as in the above example) throughout our entire program we only need one prototype factory that will contain a list of prototypes and reply to create requests based on that lists content. In these types of scenarios the best implementation is to make the prototype factory a singleton that when loaded will look into a config file, find a list of all the possible prototype objects (built-in ones and future ones) and the load all of them using reflection. As in this code sample:
class PrototypeFactory
{
private PrototypeFactory()
{
Configure();
}
private List<IPrototype> Prototypes = new List<IPrototype>();
private void Configure()
{
List<string> prototypeConfig = LoadConfig(); //load the prototype list from somewhere
foreach(string config in prototypeConfig)
{
string[] parts = config.Split(','); //let's assume each config has a dll name followed by a class name separated by a comma
//obviously we need extensive error checking and exception handling which has been omitted for the sake of brevity
Assembly asm = Assembly.Load(parts[0]);
IPrototype pt = (IPrototype)asm.CreateInstance(parts[1]);
Prototypes.Add(pt);
}
}
/* other methods including the singleton implementation */
}
You might ask why not use reflection to totally replace the prototype pattern? Actually that's one of the language mechanisms that I mentioned at the beginning of this post. But you have to remember that as always using reflection comes at a performance cost and in situations that speed and performance are at the utmost importance the above technique works better. As you can see we are only using reflection once and that is to load the initial list of prototypes. After that list is loaded to clone a prototype we are asking it for a clone and the prototype is using a standard 'new' keyword to replicate itself.
Another variation of the pattern which isn't supported directly by reflection is when the Clone method is used to do more than just create a new object. In some design problems we need the prototype object to replicate itself by first creating a new object and then initializing it with some data that the original prototype is carrying. Imagine a situation where creating new objects from scratch is going to be too expensive because each object once created would have to go to a DBMS or a remote network location to load some data to initialize itself with. In these scenarios the prototype pattern would be useful because the prototype factory will be holding a list of objects that have been created & initialized once and then when we ask it to create a new instance of one of the specific prototypes it will in return call that prototype's clone method and as mentioned above the clone method will create the new object and also copy the initialization information from its own copy into the new object (before returning it). This way we save on the expensive load process.
It's quite obvious that the above scenario can't be implemented using pure reflection and the prototype pattern needs to be in place. Here is a sample implementation of the above mentioned scenario:
class BigAndExpensivePrototype : IPrototype
{
//private constructor used internally
private BigAndExpensivePrototype(BigAndExpensivePrototype copyFrom)
{
//initialize this object based on the passed object’s data
}
//public constructor used by the prototype factory’s list creation method (should only happen once)
public BigAndExpensivePrototype()
{
//initialize this object by reading it’s information from the DBMS
}
public IPrototype Clone()
{
//create new prototype object by calling the private constructor and passing this object so that it will be initialized the easy way
return new BigAndExpensivePrototype(this);
}
}
As the above example shows the expensive routines used to load the prototype from the DBMS are called once when the prototype object is created and added to the prototype factory's list of prototypes. After that any attempt by the factory to clone and get a new copy of the prototype would result in the private constructor initializing the new object based on the original prototypes values.
Another language mechanism that one needs to consider when using this pattern is the ICloneable interface in .net. This interface is the equivalent of the IPrototype interface in the pattern's original structure. The ICloneable interface has been so closely modeled over this pattern that even the only method available in the interface is called Clone(). So the basic interface has been standardized in the .net platform (and similarly in Java) but the major decisions regarding the usage of the pattern still remain for you as the designer to make. To summarize here are the things you have to decide on:
- Is the usage of the pattern only going to be limited to creating new objects or are we going to create a new object and perform some sort of initialization based on the original prototype.
- If we are going to copy data then is it going to be a deep copy or a shallow copy (If you don't know what a deep copy or shallow copy is I'll be discussing it soon in the next post; see here)?
- If the usage of the pattern is only going to be limited to creating new objects can we afford to use reflection (is very high speed an issue: then don't use reflection)?
- How will the factory select the prototype (so far we have assumed an integer index)?
By answering the above questions you have basically determined the variation of the prototype pattern that you need to develop.
So far we have assumed that we are going to pass an integer which will map to an item in an array when calling the create method. But in many real life scenarios this isn't the case. The abstract structure of the pattern assumes that the Create method will receive a 'key' which it can translate to an appropriate prototype. For example the prototype key might be strings (and we will look them up using a Dictionary or a Hashtable) or other types of objects. So in deciding to use the pattern one of the major concerns is that there has to be a method by which the prototype factory can select the prototype objects.
In all of the above examples we have assumed a singleton for the prototype factory but there are also other situations where this pattern is useful and you can't implement the prototype factory as a singleton. For example suppose you need to configure a sub-system with a set of prototypes to use, basically you would create the prototype factory, configure its list of prototypes and then pass the prototype factory to the sub-system. The sub-system will use the factory it receives to create the objects that it needs. What the sub-system uses and how many prototypes it gets are in the calling system's power.
Now we have already seen a code example that uses the IPrototype interface but here is one that will use reflection. In this case we don't really need a base class or interface to inherit from since we don't need a Clone method to call all we need is the prototype factory to create the objects we need using reflection. Again for simplicities sake I'm going to assume that it's a singleton but you can extend this example to situations where the factory isn't a singleton object:
class PrototypeFactoryWithReflection
{
private PrototypeFactoryWithReflection()
{
Configure();
}
public static PrototypeFactoryWithReflection Instance
{
get
{
return _instance;
}
}
private static PrototypeFactoryWithReflection _instance = new PrototypeFactoryWithReflection();
private ListDefinitions = new List ();
private void Configure()
{
Definitions = //load the definition list from a config file or a similar location.
//note that we are expecting these definitions to be in the form of “dll name, class name”
}
public object Create(int idx) //again for simplicity we are assuming an index key but this can also be extended
{ //error checking code omitted for brevity
string def = Definitions[idx];
string[] parts = def.Split(‘,’);
Assembly asm = Assembly.Load(parts[0].Trim());
return asm.CreateInstance(parts[1].Trim());
}
}
As mentioned before the above implementation is using reflection to simplify the whole process and it has the advantage of not having a base interface or abstract class therefore meaning you can apply it to classes that already exist and don't inherit from IPrototype (ICloneable) but as mentioned before reflection is a bit slower then directly calling the 'new' keyword and if performance is a major issue you are better off implementing this via the standard definition of the pattern.
No comments:
Post a Comment