Tuesday, November 18, 2003

Prototype Pattern


Description

This is another pattern that builds objects in a way that protects the client from having to know the exact class of the required object. So when would you use it instead of Factory, Abstract Factory or Builder? My answer would be:

  1. When you have objects that take a lot of resources to create (e.g., if you have to read a file or database table to get values) but don't take a lot to clone.
  2. When you have a lot of immutable objects and you don't want to keep creating new instances but instead you want to use a single instance and pass around copies of it. (e.g., fonts - you will use a particular font a lot in a word processor program, but it can be the same instance every time.) This sounds like the "Singleton" pattern, which I'll describe next, but notice that I say "when you have a lot of immutable objects."
  3. When you have a large number of sub-classes and could instead make them into a single class only differing by the values of certain attributes. (e.g., you have several types of customer and you could sub-class them, but it is actually easier to create instances of the one class and give them different values to represent the different types. Create the PremierCustomer prototype once, loading the data that differentiates him from a CreditRiskCustomer, then copy that prototype and change the "Name", "Address", etc. values.)
  4. If there are a large variety of classes to be instantiated and the various instances differ only in certain attributes. (e.g., you have a game program and Monster, Hero, Villain, etc., classes. You can have lots of different monsters and they will differ in the graphic used to display them, their size, viciousness, etc. Rather than create a new Monster object every time simply copy the prototype and change the graphic, etc.)

(Note: Clone has two definitions: a "shallow clone" and a "deep clone". A shallow clone copies value type variables within the original but doesn't make a copy of reference type variables, it just returns the reference. I have also seen shallow clone used to mean that you return a reference to the prototype object - that's not strictly speaking a clone, but it does get used that way by some people. A deep clone copies everything within the prototype so the copy has no connection with the prototype - this usually takes a lot more resources than a shallow clone. Which method you use depends on your particular situation.)

Example

Let's say we have a system that inventories audio visual materials. To create the objects that represent the materials takes a lot of resources but to then customize them to the particular type of material (e.g., 1" tape) is relatively quick.

Lets start with the Material super-class:


Public MustInherit Class Material : Implements ICloneable
 
Public MustOverride Property Description() As String
 
'Returns a shallow clone
Public Function Clone() As Object Implements ICloneable.Clone
Return CType(Me.MemberwiseClone(), Material)
End Function
 
Public MustOverride Sub CommonMethod()
End Class


And here is a sub-class:


Public Class Tape : Inherits Material
'All sorts of complex objects that take a long time to initialize
Private TypeX As ComplexClassOfSomeKind
'etc ...
 
Public Overrides Property Description() As String
'code goes here
End Property
 
Public Overrides Sub CommonMethod()
'code here
End Sub
 
'other methods ...
End Class


Now we have the class that controls the creation of the prototypes and provides the clones:


Public Class MaterialBuilder
Private _prototypes As New Hashtable
 
Public Sub New()
InitializePrototypes()
End Sub
 
Public Sub InitializePrototypes()
'put objects of the different material sub-classes into the hashtable
_prototypes.Add("TAPE", MaterialFactory("TAPE"))
_prototypes.Add("CD", MaterialFactory("CD"))
_prototypes.Add("DVD", MaterialFactory("DVD"))
'etc ...
End Sub
 
Public Function BuildMaterial(aType As String) As Material
Dim builder As Material
builder = _prototypes.Item(aType)
Return builder.Clone()
End Function
 
Private Function MaterialFactory(aType As String) As Material
'factory code goes here
End Function
End Class


The pattern could be used like this:


Class ClientClass
Public Sub DoIt(aType As String, description As String)
Dim builder As New MaterialBuilder
Dim myMaterial As Material = builder.BuildMaterial(aType)
myMaterial.Description = description
myMaterial.CommonMethod()
'etc ...
End Sub
End Class

Resources

Wikipedia - Prototype Pattern
Data & Object Factory - Prototype Pattern
DotNetExtreme - Prototype Pattern

No comments: