What do CAD design tools, workflow automation, and digital audio synthesizers have in common? They can all benefit from the Prototype pattern in their software architecture.
Although the Prototype pattern achieves true glory in languages such as JavaScript, which base their entire class structures on Prototypes instead of traditional classes, the pattern still brings powerful functionality to C#. Its primary use is in a very specific niche: when the users of the application will perform design work, and then group those designs into reusable subcomponents.
Simply put, Prototypes are classes with a “Clone” operation. Users can make changes to a class as necessary, and then clone it to create a duplicate, separate version of the class. The duplicate class can then be further changed, or used elsewhere in the system to build the final design.
The clone operation is particularly useful in CAD (computer-aided design). Architects and circuit designers often re-use certain components, such as a staircase design, or an electrical component such as a counter. Instead of requiring the user to recreate the component each time, they can instead “clone” the existing component and re-use it in the drawing.
Workflow design tools, on the other hand, integrate with line-of-business applications to help users automate their work. In response to certain events, such as completion of a task, the computer might send emails or create new tasks for different users. A workflow design tool with Prototype classes allows users to recreate repetitive workflow elements, and re-use them in other sections of the business application.
Digital synthesizers are also a classic example of the Prototype. As musicians or DJs create “loops”, they save them to the system. Instead of recreating the loops from scratch each time they compose a song, they can re-use the loops and slightly modify them as they see fit in the new song.
The prototype for a simple drawing tool is implemented as follows in C#:
public class Program{
public static void Main(){
Drawing dwg_x = new Drawing();
dwg_x.Elements.Add(new Line{ pStart=new Point(0,0),pEnd=new Point(2,2) });
dwg_x.Elements.Add(new Line{ pStart=new Point(0,2),pEnd=new Point(2,0) });
Drawing dwg_main = new Drawing();
dwg_diamond.Elements.Add(dwg_x.Clone());
dwg_diamond.Elements.Add(dwg_x.Clone(new Size(2,0)));
dwg_diamond.Elements.Add(dwg_x.Clone(new Size(0,2)));
dwg_diamond.Elements.Add(dwg_x.Clone(new Size(2,2)));
}
}
public class Element {
public virtual Element Clone() { return Clone(new Size(0,0)); }
public virtual Element Clone(Size offset) { return new Element(); }
}
public class Line : Element {
public Point pStart;
public Point pEnd;
public override Element Clone(Size offset) { return new Line{ pStart=pStart+offset,pEnd=pEnd+offset }; }
}
public class Drawing : Element {
public List
public override Element Clone(Size offset) {
Drawing rslt = new Drawing();
foreach(Element e in Elements) { rslt.Elements.Add(e.Clone()); }
return rslt;
}
}
In this example, the Prototype is an “Element” class that is implemented as both a “Line” and as a “Drawing”. A Drawing can consist of multiple lines, or other drawings. The program first draws an “x” into dwg_x, and then duplicates that “x” using the Prototype four times, to create a diamond pattern into dwg_diamond.
The Prototype can also be optimized, to delay creation of the Cloned object until it is necessary when one of the member properties has changed. Although not every application can benefit from the Prototype, those that do will find an elegant architecture and simpler alternative to nested case statements and logic overhead.
Written by Andrew Palczewski
About the Author
Andrew Palczewski is CEO of apHarmony, a Chicago software development company. He holds a Master's degree in Computer Engineering from the University of Illinois at Urbana-Champaign and has over ten years' experience in managing development of software projects.
Google+