Looking for work? Check out our jobs area.


Tutorial Design Patterns - Creational patterns

Prototype pattern

Overview

Difficulty and Complexity
Degree of Difficulty 6
Structural Complexity 7
Extra-Pattern Complexity 4
Implementation Specificity 3


Intent according to the GoF
"Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype" [GoF 94]

Intent seen through Real Life
People leave jobs. Nowadays there is hardly a position which is not important to the organization, as those went out 10 years ago with Business Process Re-engineering, right-sizing, restructuring and the like. The only way you could hold a sinecure these days is if you are the boss's son or niece by his oldest brother's side. So when a programmer leaves a job, chances are the position needs to be filled and filled pretty quickly. The HR manager might advertise for someone with a BSc in Computer Science and a specialty in Software Development from a recognized university, five years of programming experience in a manufacturing environment with 2 years in SAP, strong analytical experience and very good people skills. Now the chances of finding the exact person you advertised for is usually very slim. The chance of someone floating around unemployed, who is the exact twin of the person who left, gliding through the ether and landing straight into the position to take over where the old employee left off is even slimmer. So in reality, the HR Manager is looking for someone who matches the profile as closely as possible. Because that's why they study HR right? To be able to interview a couple of folks who match the profile somewhat and subsequently figure out which candidate best meets the requirements. On-the-job exposure will finish molding the person into just what is required.

That's the prototype pattern at work. It suggests that when you have a requirement for a product-class, instead of trying to create the object from scratch, you pick the best candidate from a set of possible matches and you tweak the object to fit the exact characteristics required.

When designing wheels, the perfect circle is the best design. You may wrap it with some rubber containing air and some interesting looking grooves on the outside it, or carve some cogs on its surface, but it's still just a wheel.

So the idea behind the prototype pattern is to create a new object by starting with one of several basic designs which have most of the features which you currently need to be in the new object. The client will then tweak the object which is returned so that it looks and behaves exactly in the manner that is required for the current situation.

In the prototype pattern, we have several prototype-classes which are classes with basic features that we may want-already built in. Each prototype is a point-of-departure for coming up with the specific design which we require. But using one of the prototypes saves us from having to construct the product from scratch. We just make a few changes here and there and we have met the requirements.

Sitting between the client and the prototypes are the prototype-manager and the Abstract-Prototype. The prototype manager is just a class which holds a reference to the Prototypes which are available for use by the client, (much like a catalogue has a set of pictures of products the mail-order shopper can order). The client will forward requests to the prototype manager to return one of the prototypes it "knows" about. There is usually a bit of logic which the Prototype manager performs to figure out which Prototype to return, but that's the basic job of the prototype manager.

Now here's the trick with the Prototype pattern. The Abstract-Prototype. This class governs the behaviour of the Prototypes and ensures that they implement a method for copying or "cloning" themselves along with the functionality that the client is actually interested in. If you are going to make a lot of changes to the prototype after you get access to it, and the same prototype may be needed-untouched-by another client later on, then you have to ensure that the first client gets a true copy of the prototype and not just an object reference to the same prototype.

Think of the difference between a true-copy (called a deep copy) and a reference-copy (called a shallow-copy) like this: When signing in at work, you get a "copy" of the signing sheet in order to sign. This is like a shallow copy. Everyone ends up signing on the same "copy". An application for vacation-leave, however is unique to you, so you will want to photocopy the blank leave-form and then fill it in. So the copy you use is a fresh copy, unconnected to the original and so changes to the copy you have do not affect the original. Later on we will see deep and shallow-copying in action and talk about how to do them, why to do them and some implications surrounding them.

Intent seen through O/S & Applications
If you look at the new document dialog box in Word or Excel, you will see the prototype pattern in principle. When creating a Fax Letter head, you wouldn't want to start from scratch. Instead you will select from one of the templates provided with Word.. The user simply customizes the basic document to make it fit the exact purpose intended, by filling in the addressee's name, phone number, the date and so on.

In this case, the concrete-prototypes are the individual FAX templates, the Prototype manager is the Templates Dialog box, the client is your new Word FAX cover page and the abstract-prototype is the requirements which all Fax-Templates must meet (specification for headings etc)

http://www.programmersheaven.com/articles/designpatterns/image020.gif

The Meat of the Matter

Scenario - Sample Code
In Jamaica, when a music producer or studio engineer comes up with a really hot rhythm, many DJs (equivalent of a rapper in the US) tend to jump on the bandwagon and would "voice" a tune on the rhythm. Over the years we have had popular rhythms such as the "Taxi", "Sleng Teng", "Pepper Seed", "BackYard" and others. The idea is that since the rhythm is hot, if you get a popular DJ to voice some hot lyrics on it, then you're sure to have a hit. In the very "hit" and miss field of popular music, you can imagine why the producers don't like to try unproven rhythms since they cost a lot of money and many songs end up on the heap of un-popularity.

UML - General
http://www.programmersheaven.com/articles/designpatterns/image021.gif

UML - Sample Code
http://www.programmersheaven.com/articles/designpatterns/image022.gif

Participants - Sample Code
Abstract Prototype[/italic] - HotRhythm and ensures that the Concrete-Prototypes implement the ICloneable Interface. Also ensures that they have a play method. Since it is an interacting interface, concrete-products are returned wrapped up in it so as to be unconcerned with the actual concrete-product at work.

Concrete Prototype[/italic] - BackYard,PepperSeed, Taxi These are the products which the client are interested in, which will be used in it operations. Implements the Abstract-Prototype and hence are able to clone themselves and do the useful work which the client requires.

PrototypeManager[/italic] - RhythmProtoTypeManager In charge of all Prototypes in the system. Keeps a reference to the available prototypes and implements logic that decides which prototype to clone and return when requested by the client. Client - Module1 Makes use of the Prototypes. Instantiates Prototype manager and invokes the factory method on the Prototype manager. This factory method returns the Prototype which matches the parameter passed to the method.

Sample Code (Highlights)
Prototype manager maintains a datastructure which contains references to all the available prototypes. If the client requests a particular prototype by calling its MixRhythmTrack method, then one of two things will happen. If a reference to the required prototype is already in the hashtable, then it clones the required-prototype and returns it to the client. If the required prototype is not instantiated, then it instantiates it singleton-style and places it in the hashtable . It then clones the concrete-prototype and returns the copy to the client.

Prototype Manager also implements selection logic which interprets the RhythmName parameter passed to the MixRhythmTrack method in order to figure out which one of the available prototypes is required by the client.

PrototypeManagers - RhythmProtoTypeManager

Public Class RhythmProtoTypeManager  ' Prototype Manager
    Dim RyhthmTape As New Hashtable   ' Data structure for storing Prototypes
    Function MixRhythmTrack(ByVal RhythmName As String) As Ryhthms.HotRhythm  'Returns the appropriate prototype based on the parameter passed to it.
        Dim BackYardRhythm As Ryhthms.BackYard              'Declare a prototype
        Dim PepperSeedRhythm As Ryhthms.PepperSeed     'Declare a prototype
        Dim TaxiRhythm As Ryhthms.Taxi                               'Declare a prototype
        Dim RhythmToMix As Ryhthms.HotRhythm
        Dim SelectedRhythm As Ryhthms.HotRhythm
         'Prototype selection logic
        Select Case UCase(RhythmName)               
' In case we want the BackYard Rhythm,
            Case "BACKYARD"                             
   ' and theBackyard Rhythm is not yet instantiated,
                If RyhthmTape("BackYardRhythm") Is Nothing Then  
        ' then instantiate it and add to the DataStructure
                     RyhthmTape.Add("BackYardRhythm", New Ryhthms.BackYard)                         
                     ' Return the existing BackYard Rhythm to the client
                       SelectedRhythm = RyhthmTape("BackYardRhythm")       
                End If
            Case "PEPPERSEED"           '  Same as for BackYard
              :
              :
              :
            Case "TAXI"                            '  Same as for BackYard and PepperSeed
              :
              :
              :
        End Select
       ' Finally, whatever Rhythm we select, we need to clone it.
       ' Cloning produces a generic object, so we have to cast the cloned object to typeof Rthyhm
        RhythmToMix = CType(SelectedRhythm.Clone, Ryhthms.HotRhythm)      
        Return RhythmToMix
    End Function
End Class


Prototypes - RhythmsPrototypes

Prototypes are the product-classes in the pattern. Note below, the object-serialization approach to cloning the prototype. This ensures a deep copy.

Namespace Ryhthms
    Public MustInherit Class HotRhythm  Implements ICloneable
    'Abstract Prototype - Ensures that classes which inherit it can clone themselves
        Protected m_DrumLevel As Integer
        Protected m_BassLevel As Integer
        Protected m_GuitarLevel As Integer
        Protected m_BassLine As String
        Protected m_DrumTrack As String
        Protected m_GuitarTrack As String
        ' Implementation of the Clone method provides for the copying of the object
        Public Overridable Function Clone() As Object Implements System.ICloneable.Clone  
            Dim HotRhythmClone As Object
       ' Deep copy of the object acheived through object serialization/deserialization
       ' Start of Object Serialization Routine
            Dim MemStream As New MemoryStream(1000)
            Dim BinFormatter As New BinaryFormatter
            BinFormatter.Serialize(MemStream, Me)
        ' End of Object Serialization Routine
        ' Start of Object DeSerialization Routine
            MemStream.Seek(0, SeekOrigin.Begin)
            HotRhythmClone = BinFormatter.Deserialize(MemStream)
        ' End of Object DeSerialization Routine
        ' HouseKeeping
            MemStream.Close()
                    Return HotRhythmClone
        End Function
    End Class

   'Concrete Prototype - actual product class which is returned
    ' to the client to carry out functions required.
    Public Class PepperSeed     
        Inherits HotRhythm
        Friend Sub New()
            m_DrumLevel = 5
            m_BassLevel = 7
            m_GuitarLevel = 4
            m_BassLine = "Dooo, Doo, dup"
            m_DrumTrack = "Dumm, Dumm, Dup, Pish"
            m_GuitarTrack = "Ding, Di, Ding, Ding"
        End Sub
    End Class

   'Concrete Prototype
    Public Class BackYard    
        Inherits HotRhythm
          :
          :
    End Class
   'Concrete Prototype
    Public Class Taxi    
        Inherits HotRhythm
          :
          :
    End Class
End Namespace


Client - Module1

Module Module1
    Sub Main()
        ' Client instantiates the Prototype Manager for Rhythms
        Dim RhythmSelections As New RhythmProtoTypeManager
        Dim SongBeingMixed As New Song
        SongBeingMixed.WordsToSong = New Lyrics
        ' Activate method on PrototypeManager which returns the required Rhythm.
        '   this is a deep copy so we can change the Rhythm to our heart's desire
        SongBeingMixed.Rhythm = RhythmSelections.MixRhythmTrack("PepperSeed")
        Console.WriteLine("The singer is : {0}", SongBeingMixed.Singer.GetType)
    End Sub
End Module    


Opportunities for and Costs of - Adaptation and Extension
New concrete prototypes can be added easily since just two things need to be done:
  • Add the actual concrete class which represents the new prototype including the internal constructor-logic.
  • Register the new class in the prototype factory by adding it to the logic for instantiation based on the parameter passed to the prototype factory. For example, if we added a "Sleng Teng" Rhythm, we would need to modify the RhythmProtoTypeManager's MixRhythmTrack method. We would add the following code:
Case "SLENGTENG"                             
          If RyhthmTape("SlengTeng") Is Nothing Then  
        RyhthmTape.Add("SlengTeng ", New Ryhthms.SlengTeng)                         
                       SelectedRhythm = RyhthmTape("SlengTeng ")       
                End If

Making sure you get it right

Interface issues
The Abstract-Prototype is an Interacting-interface which allows the client to use the Rhythm in a manner which is independent of the actual rhythm returned behind the interface. Whether the Abstract-Prototype is an interface or an abstract class will be determined by the circumstances. In the case of machine-form-factors, you can see that an abstract class would be more appropriate since particular features would be shared such as the Power-Supply type, fan-type, motherboard type etc. Clearly since abstract classes allow you to reuse a lot of the code and store state, then abstract classes in this instance make more sense. It comes down to how much of it is really reusable. If the prototypes are very dissimilar and have no state, then use interfaces since they make for greater pattern-safety as method signatures are more guaranteed to be implemented.

Namespace/Scope/Accessor issues
As in the patterns before, make the Prototype-Factory and the Abstract-Prototype's constructors public. Also make the Concrete-Prototypes' constructors Friend.

Additionally, separate the client into its own assembly so that it cannot access the concrete-prototype's constructors.

Common mistakes in Pattern Literature / Pitfalls to avoid
Two mistakes are made regularly. To avoid them:

  1. Understand the issue of deep and shallow copying. By simply assigning an object to another object reference, you are making a shallow copy. A change to the object through any of the references changes the state of the object. Also doing a Member-wise Clone makes a deep copy of the primitives such as integers, but a shallow copy of all inner-objects.
  2. Decide whether to do a deep or shallow copy based on how the client uses the concrete-prototype over its lifetime. Don't take it for granted.
  3. Don't assume that the effort of making a deep copy is a good thing. Make sure that deep copying is both necessary (to avoid incurring unnecessary performance penalties) and correct. Remember that by making deep copies of the prototype, you might be hiding changes made by one client, from other clients which need to see that change.
See the Useful Variations section for a more thorough discussion.

Good Examples in Pattern Literature
[PKu 04]

Explanation Code


[LVS 03]

Explanation Code

Advanced Issues

Useful Variations
Check the depth before you dive!

A tiny-tot's Olympic diving pool is a giant's wading pool. Nowhere is this more apparent than in the Prototype pattern. A big issue with the Prototype pattern is deep and shallow, at least as far as cloning is concerned. Remember that when you make a copy of an object by using memberwise cloning, only primitive data types get a true copy made of themselves. In the case of object data-types, a shallow copy is made. In other words you have only made a copy of the reference and assigned it to the new object-variable. So that if you modify the object properties, then the object pointed to by both variables is changed. This needs to be compensated for or guarded against based on what is expected to happen to the concrete-prototype during its object-lifetime.

There are many possible scenarios which can be taken into account and compensated for with a mixture of techniques. Lets look a couple of issues and some possible approaches.

1a. Personal-Prototype Only one client references the prototype each time the program runs, but changes it. In this case deep copying is a waste since changes to the prototype do not affect other clients.

1b. NewsMedia-Prototype The prototype is never modified but several clients may reference it. If this is guaranteed to be the case, then you should probably make the properties read-only. This ensures that there is no "rogue-state object" to violate the agreements set down by the community.

1c. Environmental-Prototype The prototype is accessed and modified by several clients, each client is interested in the changes made by other clients The prototype should clearly be read/write but deep copying should not be done. Since the prototype is automatically implemented as a singleton, returning a shallow copy ensures that changes made by one client are seen by subsequent clients. In this particular instance deep copying is actually dangerous since it would allow several instances to exist which may or may not have the latest changes. This is equivalent to losing uncommitted changes in a database because someone else read the same record you have in memory, but made and saved changes which were committed after you committed your changes. Of course modern databases are not susceptible to snafus such as this, but you get the picture.

1d. Evolution-Prototype Changes made to the prototype should be persisted so that future program executions use the updates. In order to persist the changes so future program executions do not start building the concrete-prototypes from scratch and overhauling the prototype each time, you will need to persist the object state by saving the state to a database or XML. Each time the program runs and instantiates a Prototype, it does so by reading back the most current data from the database or XML file. The builder pattern would possibly be useful in helping to re-create prototype each time before its actual use.

Advanced Discussion
Of Tweaks and Overhauls
This bis question is, what is a tweak and what is a fundamental change? Answering this question is important since the Prototype-pattern is only relevant when you are making slight tweaks to the prototype being returned, not fundamental overhauls. There are probably two things which will help to answer the Overhaul/Tweak question.

First, how much time does it take to tweak the object into shape? If it's a lot of time, for example if you have to go to a database for information to complete configuring the object, then that's probably in overhaul territory. In that case the prototype pattern is probably wasted on the situation since you are keeping a copy of "ready-made" objects which still need expensive alterations, thereby incurring overhead for nothing. Possibly the Builder would be better suited to that situation.

The second issue is the reliability of the construction process. Sometimes the less you have to mess with the object the better, so even though you may have to change 90% of the object at run-time, the first 10% of the creation process may be so risky that you still don't want to mess with it on a case-by-case basis. However in that case you may need the Flyweight pattern more.

Flyweight is recommended for situations where the instantiation of an object is expensive because you need lots of copies of it. But in the case where any instantiation of even 2 or 3 objects can be risky and cause catastrophic crashes, then the risk of creating 2 or 3 risky objects becomes equivalent to the performance-cost of making 10,000 normal objects. Both scenarios are expensive, just that one is expensive in terms of memory used while the other is expensive in terms of the risk involved. The Flyweight pattern would therefore apply.

Different ways of filling the object pool
As if all the above was not enough to chew on, well consider this. There are three ways you can effect a deep copy. Speed and maintainability will of course vary for each method.

  • Instantiate the new object by assigning the values to the second-object, one primitive at a time. So you might end up with a lot of code such as:
OuterClass2.InnerClass1.Variablea = OuterClass1.InnerClass1.Variablea 
   :
OuterClass2.InnerClass4.Variablec = OuterClass1.InnerClass4.Variablec


This is programmatically tedious and dangerous. If you add more primitives to the class and the code for cloning is not updated, then the clone is not properly made to reflect the components which were added recently. Use this approach only if the object's composition is very stable and there are very few primitive member variables.

  • Create several structures within your class. The structures will contain references to all possible primitive members in the old copy. When cloning, assign the value of all structures in the old copy to the structures in the new copy. This is a lot cleaner, just make sure to maintain the class by adding all new variables to one of the existing structures and not directly to the body of the class.
'  All internal variables of the structure are copied
OuterClass2.InnerStructure1 = OuterClass1.InnerStructure1 
OuterClass2.InnerStructure2 = OuterClass1.InnerStructure2 


  • Use object serialization to make a memory stream out of the old copy of the prototype and instantiate the new copy by de-serializing the memory stream. This is safest but speed might be sacrificed. It is also technically hardest to implement and understand, though easiest to maintain since there is absolutely nothing to update in the cloning method, regardless of how many changes you might make to variables, structures or inner classes in your main class.
Explanation of Pattern subscripts
R Clients can make use of the Prototype factory with ease. They just need to know what string relates to what concrete-prototype. In fact if we used enumerated types, this would [/b]make it even easier.
E Concrete-Prototypes can be added with ease.
S- When cloning objects using object serialization, make sure you don't compromise the privacy of the data in your objects by leaving scraps of XML or file streams on hard-disk.

Downloads
Download design patterns sourcecodeDownload Sample sourcecode
Download design patterns sample databaseDownload Sample database
Download Sample design pattern UML diagramsDownload Sample UML diagrams




David M. Simmonds and Bibliography.about David M. Simmonds


Back to the Design Patterns in VB.NET main page



 

Other Views

corner
Popular resources and forums for programmers on Programmersheaven.com
Assembly, Basic, C, C#, C++, Delphi, Java, JavaScript, Pascal, Perl, PHP, Python, Ruby, Visual Basic
© Copyright 2009 Programmersheaven.com - All rights reserved.
Reproduction in whole or in part, in any form or medium without express written permission is prohibited.
Violators of this policy may be subject to legal action. Please read our Terms Of Use and Privacy Statement for more information.
Publisher: Lars Hagelin. Read the latest words from the publisher here.
Be the first to sign up for Lars Hagelin’s In-depth Outsourcing Newsletter here.
bootstrapLabs Logo A bootstrapLabs project.