Chapter 3. Prototype

Back in the old days when printing wasn't common, people used to use some sort of wooden stamps (later they became rubber stamps) to print some commonly used graphics and text on paper. Many years later, people realized that by combining different commonly used stamps, it could be one of the easiest ways to mass reproduce the same information on paper. Without using the same stamps for printing the same glyphs on paper, distribution of information and knowledge would be much more expensive and time-consuming.

In many object-oriented applications, there are objects that are just too expensive or complex to create. It would be a lot easier if we could recreate the same object with slight changes only. A typical example could be cloning a composite structure (e.g., a tree structure). It's very difficult to construct another tree composite from scratch. Rather we reuse the one that is available with slight modification to fit a particular situation in the program.

If we can turn some objects into rubber stamps and tell them to create a clone of themselves, it can save a lot of effort to build them. It's highly reusable and more maintainable than creating different individual classes with very few differences from the parent class.

The pattern that applies to the "clone" operation is called the Prototype pattern. Cloning is to manufacture a line of products with the same mold. An item that a mold is based on is a prototype. A prototype determines what a final product should be. Some attributes, like color and dimensions, could be slightly different despite the fact that the products are copied from the same mold. Despite the small differences, they are still of the same kind.

In this chapter, we are going to discuss some issues related to object cloning in general and then how to implement the Prototype pattern for cloning complex objects in Objective-C.

What Is the Prototype Pattern?

The Prototype pattern is one of the simplest design patterns. A client knows an abstract Prototype class. At runtime any object that is a subclass of the abstract Prototype can be cloned at the client's will. So the client can make multiple instances of the same type without creating them manually. A class diagram that shows their static relationships is illustrated in Figure 3-1.

A class diagram of the Prototype pattern

Figure 3-1. A class diagram of the Prototype pattern

Prototype declares an interface for cloning itself. ConcretePrototype as a subclass of the Prototype implements the clone operation for cloning itself. The client here creates a new object by asking a prototype to clone itself.

Note

THE PROTOTYPE PATTERN: Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.[1]

When Would You Use the Prototype Pattern?

We may think about using the Prototype pattern when

  • We need to create objects that should be independent of what they are and how they are created.

  • Classes to be instantiated are determined at runtime.

  • We don't want to have a hierarchy of factories for a corresponding hierarchy of products.

  • The differences between instances of different classes are just a few combinations of state. Then it's more convenient to clone a corresponding number of prototypes rather than instantiating them manually.

  • Classes are not easy to create, such as composite objects in which each component can have other components as children. It'd be easier to clone existing composite objects and modify the copies.

Note

A common misconception about using the Prototype pattern is that a prototype object should be an archetypal object and, typically, one that is never actually used. That misconception focuses on a particular way to implement the pattern. From a functional point of view, a prototype object can be any object that should be better off cloning itself than getting instantiated manually. There are two particularly common situations where we would naturally think about using the pattern

  1. You have many related classes whose behavior is slightly different, and they mostly differ in internal attributes, such as name, image, etc.

  2. You need to use a composite (tree) object as a basis for something else, e.g., use a composite object as a building block to build another composite object.

There are a lot more situations where you'd apply this pattern in the real world. Using design patterns is more art than science. Break some rules, be creative, and work smarter!

The bottom line for this pattern is to make a true copy of an object so we can use it as a basis (prototype) for something else related in the same context. So our next stop is to discuss issues related to copying objects.

Shallow Copying vs. Deep Copying

If an object has a pointer as a member variable that points to some resource in memory, how would you make a copy of that object? Would you just copy that pointer value and pass it to a new object as a clone? The pointer is just a placeholder that stores an address of some resource in memory. In a cloning operation, if a pointer is just copied over to a new object (a clone), the underlying resource is still actually being shared by both instances, as illustrated in Figure 3-2.

An illustration depicts a scenario of cloning ConcretePrototype1 by just copying the value of the Resource Pointer 1, but the actual resource is not copied.

Figure 3-2. An illustration depicts a scenario of cloning ConcretePrototype1 by just copying the value of the Resource Pointer 1, but the actual resource is not copied.

In the clone operation of ConcretePrototype, it copies a pointer value of the Resource Pointer to a new clone. Even though the instance of ConcretePrototype made another instance of the same type as its clone, the pointers in both instances are still pointing to the same resource in memory. So the pointer value is cloned but not the actual resource. We call this shallow copying.

So what's deep copying then? Deep copying is to copy not just pointer values over but also the resources being referenced by the pointers. A deep copying version of the same clone operation is illustrated in Figure 3-3.

The clone operation is to not just simply make a copy of the Resource Pointer to a new one but also to make a true copy of the actual resource in memory. So the pointer of the clone is pointing to a copy of the same resource (content) in memory but in a different location.

This is a similar scenario as in Figure 3-2, but the actual resource in memory is copied during the cloning operation.

Figure 3-3. This is a similar scenario as in Figure 3-2, but the actual resource in memory is copied during the cloning operation.

Using Object Copying in the Cocoa Touch Framework

The Cocoa Touch framework provides a protocol for any NSObject descendent to implement deep copying. Any subclass of NSObject needs to implement the NSCopying protocol and its method, (id)copyWithZone:(NSZone *)zone. NSObject has an instance method called (id)copy. The default copy method calls [self copyWithZone:nil]. For subclasses that adopt the NSCopying protocol, that method needs to be implemented, otherwise it will raise an exception. In iOS, this method retains the newly cloned object before returning it. The invoker of this method is responsible for releasing the returned object.

Most of the time, an implementation for deep copying doesn't look very complicated. The idea is to copy any necessary member variables and resources, pass them to a new instance of the same class, and return the new instance. The trick is to make sure you do really copy the resource in memory and not just pointer values. It's more apparent when you need to deal with a composite structure like the one for the TouchPainter app in Chapter 2.

Implementing the Copy Method for the Mark Aggregate

In our original TouchPainter app, one of the most important data structures is the Mark aggregate object that contains all Dots and Strokes instances for a scribble. There are at least two situations that require the cloning of the whole structure:

  • Make a deep copy of it and archive it.

  • Reuse the same scribble as a "pattern template" (rubber stamp) to repeat the same pattern as the user draws.

For the first one, we need to make sure the copy won't be modified while it's being kept for archiving or other related processes as compared to just retaining a reference to the original aggregate (see Memento pattern in Chapter 23 for details).

If we allow the user to reuse the same scribble as a basis for other strokes, then we need to let the current scribble copy itself so it can be used as a "pattern template" feature.

We are going to adopt a composite structure (Composite pattern, Chapter 13) that contains the user's strokes in the TouchPainter app. The composite structure is illustrated in Chapter 2 as an integral part of the app. The parent of the structure called Mark is defined as an Objective-C protocol. Mark has three concrete classes (components) that define a composite object at runtime. Dot and Vertex define individual components while Stroke defines composite components that can also contain other Mark instances as children. Dot is a subclass of Vertex. The challenge here is to make an exact deep copy of the whole Mark aggregate structure without knowing how to parse the whole tree. A Stroke object can contain objects of Dots or its subclasses as well as other Stroke objects. We need to modify the original Mark and its implementing classes to have a "copy" method that will do the recursive deep copying trick. Figure 3-4 illustrates a class diagram of the Mark composite classes with a copy method.

There is not much change in the interfaces of Mark and its concrete classes except a copy method is added to each of them. There is a fictitious client that does savePattern with a currentMark as a copy called pattern for later use. How to use the copies depends on the application, but the rule of thumb is we ask objects to create themselves when it's too complicated to recreate by a client, like reconstructing an existing Mark composite object, for example.

A class diagram of the Mark composite classes implementing the Prototype pattern

Figure 3-4. A class diagram of the Mark composite classes implementing the Prototype pattern

Now let's look at some code.

Listing 3-1 illustrates changes in the Mark protocol.

Example 3-1. Mark.h

@protocol Mark <NSObject>

@property (nonatomic, retain) UIColor * color;
@property (nonatomic, assign) CGFloat size;
@property (nonatomic, assign) CGPoint location;
@property (nonatomic, readonly) NSUInteger count;        // number of children
@property (nonatomic, readonly) id <Mark> lastChild;

- (id) copy;

- (void) addMark:(id <Mark>) mark;
- (void) removeMark:(id <Mark>) mark;
- (id <Mark>) childMarkAtIndex:(NSUInteger) index;

@end

We have added a new copy method to the Mark protocol that returns an instance of an implementing class.

There are a few implementing classes that will implement the copy method. Classes of their objects that will be part of a Mark aggregate at runtime are Vertex, Strokes, as well as a subclass of Vertex, Dot. Dot objects are to represent a single dot drawn on the screen as opposed to a Stroke object. A Stroke object can be the grandparent of all types of Mark as well as just a simple parent that contains Vertex objects to form a complete stroke. Vertex doesn't do anything except maintain the location (coordinates) on the screen, so a rendering algorithm can draw them as a stroke (see Composite pattern in Chapter 13 and Visitor pattern in Chapter 15).

A visual diagram that depicts their relationships is illustrated in Figure 3-5.

The relationships between Dot, Stroke, and Vertex objects as a composite structure

Figure 3-5. The relationships between Dot, Stroke, and Vertex objects as a composite structure

We will continue to implement the copy method for the implementing classes of the Mark protocol. The first one is Vertex in Listing 3-2.

Example 3-2. Vertex.h

#import "Mark.h"

@interface Vertex : NSObject <Mark, NSCopying>
{
  @protected
  CGPoint location_;
}

@property (nonatomic, retain) UIColor *color;
@property (nonatomic, assign) CGFloat size;
@property (nonatomic, assign) CGPoint location;
@property (nonatomic, readonly) NSUInteger count;
@property (nonatomic, readonly) id <Mark> lastChild;

- (id) initWithLocation:(CGPoint) location;
- (void) addMark:(id <Mark>) mark;
- (void) removeMark:(id <Mark>) mark;
- (id <Mark>) childMarkAtIndex:(NSUInteger) index; // needs to be added to draft

- (id) copyWithZone:(NSZone *)zone;

@end

Wait a minute—why is there no copy method and copyWithZone: instead? And why is there a copy method in the Mark protocol but not the copyWithZone: method? The Mark protocol adopts the NSObject protocol while Mark's concrete classes adopt Mark and subclass NSObject class. The NSObject protocol doesn't have a copy method declared, but NSObject does. When a message of copy is invoked on a receiver of NSObject, NSObject will in turn forward the call to its subclass that adopts the NSCopying protocol. The subclass needs to implement the required copyWithZone:zone method defined in NSCopying to return a clone of itself. If the subclass doesn't implement the method, an instance of NSInvalidArgumentException will be thrown. That's why we need to let the Vertex class adopt the NSCopying protocol and implement its copyWithZone:zone method for the cloning process. However, the NSObject protocol doesn't have a method copy declared, so we also declared it in our Mark protocol to avoid compiler warnings. Vertex is a subclass of NSObject, so it implements that copyWithZone: method as in Listing 3-3.

Example 3-3. Vertex.m

#import "Vertex.h"


@implementation Vertex
@synthesize location=location_;
@dynamic color, size;

- (id) initWithLocation:(CGPoint) aLocation
{
  if (self = [super init])
  {
    [self setLocation:aLocation];
  }

  return self;
}

// default properties do nothing
- (void) setColor:(UIColor *)color {}
- (UIColor *) color { return nil; }
- (void) setSize:(CGFloat)size {}
- (CGFloat) size { return 0.0; }

// Mark operations do nothing
- (void) addMark:(id <Mark>) mark {}
- (void) removeMark:(id <Mark>) mark {}
- (id <Mark>) childMarkAtIndex:(NSUInteger) index { return nil; }
- (id <Mark>) lastChild { return nil; }
- (NSUInteger) count { return 0; }
- (NSEnumerator *) enumerator { return nil; }

#pragma mark -
#pragma mark NSCopying method

// it needs to be implemented for memento
- (id)copyWithZone:(NSZone *)zone
{
  Vertex *vertexCopy = [[[self class] allocWithZone:zone] initWithLocation:location_];

  return vertexCopy;
}

@end

In the overridden copyWithZone: method, a new instance of Vertex is created with [[self class] allocWithZone:zone] and initialized with the current location. [self class] is used because we want its subclasses to be able to reuse this copy method as well. If directly used [Vertex alloc], then its subclasses will just return a copy of Vertex but not its own actual type. Vertex only implements the location property, so once the new copy is initialized with the current location then vertexCopy is returned.

Vertex objects are used for composing strokes and don't contain any other information such as color and size. But when there is a single point created on the screen by the user, we need another type of data structure to contain color and size to represent that dot. Besides color and size, location is also crucial to an individual dot. So we create a subclass of Vertex called Dot as shown in Listing 3-4.

Example 3-4. Dot.h

#import "Vertex.h"

@interface Dot : Vertex
{
  @private
  UIColor *color_;
  CGFloat size_;
}

@property (nonatomic, retain) UIColor *color;
@property (nonatomic, assign) CGFloat size;

- (id) copyWithZone:(NSZone *)zone;

@end

Similar to Vertex, it implements the copyWithZone: method to support copying in conjunction with the protocol. Its implementation of the method is shown in Listing 3.5.

Example 3-5. Dot.m

#import "Dot.h"


@implementation Dot
@synthesize size=size_, color=color_;


- (void) dealloc
{
  [color_ release];
  [super dealloc];
}

#pragma mark -
#pragma mark NSCopying delegate method

- (id)copyWithZone:(NSZone *)zone
{
  Dot *dotCopy = [[[self class] allocWithZone:zone] initWithLocation:location_];

  // copy the color
  [dotCopy setColor:[UIColor colorWithCGColor:[color_ CGColor]]];

  // copy the size
  [dotCopy setSize:size_];

  return dotCopy;
}


@end

The first initialization statement is almost the same as Vertex. Dot supports two more attributes types, so it also needs to set the color and size properties with its own color_ and size_ private variables on top of location. Both location and size can be assigned to a copy with zero concern. However, color needs some attention here. We need to make a copy of UIColor with an existing CGColor value in the color_ variable. Finally the method returns dotCopy.

Let's move on to the Stroke class as shown in Listing 3-6.

Example 3-6. Stroke.h

#import "Mark.h"


@interface Stroke : NSObject <Mark, NSCopying>
{
  @private
  UIColor *color_;
  CGFloat size_;
  NSMutableArray *children_;
}
@property (nonatomic, retain) UIColor *color;
@property (nonatomic, assign) CGFloat size;
@property (nonatomic, assign) CGPoint location;
@property (nonatomic, readonly) NSUInteger count;
@property (nonatomic, readonly) id <Mark> lastChild;

- (void) addMark:(id <Mark>) mark;
- (void) removeMark:(id <Mark>) mark;
- (id <Mark>) childMarkAtIndex:(NSUInteger) index;
- (id) copyWithZone:(NSZone *)zone;

@end

Then let's see its implementation in Listing 3-7.

Example 3-7. Stroke.m

#import "Stroke.h"


@implementation Stroke

@synthesize color=color_, size=size_;
@dynamic location;

- (id) init
{
  if (self = [super init])
  {
    children_ = [[NSMutableArray alloc] initWithCapacity:5];
  }

  return self;
}

- (void) setLocation:(CGPoint)aPoint
{
  // it doesn't set any arbitrary location
}

- (CGPoint) location
{
  // return the location of the first child
  if ([children_ count] > 0)
  {
    return [[children_ objectAtIndex:0] location];
  }

  // otherwise returns the origin
  return CGPointZero;
}

- (void) addMark:(id <Mark>) mark
{
  [children_ addObject:mark];
}

- (void) removeMark:(id <Mark>) mark
{
  // if mark is at this level then
  // remove it and return
  // otherwise, let every child
  // search for it
  if ([children_ containsObject:mark])
  {
    [children_ removeObject:mark];
  }
  else
  {
    [children_ makeObjectsPerformSelector:@selector(removeMark:)
                               withObject:mark];
  }
}

- (id <Mark>) childMarkAtIndex:(NSUInteger) index
{
  if (index >= [children_ count]) return nil;

  return [children_ objectAtIndex:index];
}


// a convenience method to return the last child
- (id <Mark>) lastChild
{
  return [children_ lastObject];
}


// returns number of children
- (NSUInteger) count
{
  return [children_ count];
}


- (void) dealloc
{
  [color_ release];
  [children_ release];
  [super dealloc];
}

#pragma mark -
#pragma mark NSCopying method


- (id)copyWithZone:(NSZone *)zone
{
  Stroke *strokeCopy = [[[self class] allocWithZone:zone] init];

  // copy the color
  [strokeCopy setColor:[UIColor colorWithCGColor:[color_ CGColor]]];

  // copy the size
[strokeCopy setSize:size_];

  // copy the children
  for (id <Mark> child in children_)
  {
    id <Mark> childCopy = [child copy];
    [strokeCopy addMark:child];
    [childCopy release];
  }

  return strokeCopy;
}


@end

The Stroke class implements the same copyWithZone: method as the Vertex and Dot classes. But for Stroke, it needs to copy not only attributes that are defined in Mark but also its children (a for loop in the foregoing code). Each child in the children_ collection variable needs to make their own copy first, and then a Stroke object will add that cloned child to a cloned strokeCopy as a new child. When everything is cloned and set, it will return the strokeCopy as a clone of the original Stroke object.

You may have noticed that the location property that was declared in the Mark protocol is now defined as @dynamic in the @implementation scope of Stroke. A stroke doesn't keep its own location on a view but just returns the location of its first child, if there is one. There are two overridden accessor methods for the location property to handle the special case. setLocation: doesn't do anything, while location returns the screen coordinates of the stroke's first child. We use @dynamic to tell the compiler not to generate those accessor methods for us as opposed to using @synthesize because we create own version of them. The details of the composite structure are discussed in Chapter 13.

We know that a Stroke object can contain any Mark reference as a child. A particular type is the Vertex; its purpose is to provide the location information to a drawing algorithm. Vertex is a direct subclass of Dot. Dot already has that copyWithZone: method implemented, as shown in Listing 3-3.

Now we have all the copying hocus-pocus set up, so we are ready to discuss how to use the prototype pattern to implement the "Pattern Template" feature.

Using a Cloned Mark As a "Pattern Template"

Now each subclass of Mark is clonable and able to return a true copy of it. One important use of object copying is for saving the state of it. In other words, a snapshot of what the object is at the moment when it is cloned (copied). When does it need to be copied? Undo and redo. Before executing an operation, the state of an object (e.g., a document) needs to be saved in some way. Then the state is pushed to an undo stack. When an undo operation is requested, the last state of the object (document) will be restored back to the application. Most of the time, copying objects is complicated and error-prone. If clients handle the process, the client code may need to be changed every time there are some changes in a target class (e.g., document again). When we are talking about copying objects, we may be thinking only about some simple objects with just a few member variables and interfaces. However, things can go pretty wild when you need to deal with composite objects. It would be very complicated to clone a composite object just like that. A composite object could be very heavy-weighted and tricky to handle. You can picture traversing the whole structure and making new instances of each node with corresponding attributes. What's even worse is that we don't know the exact type of each node in the structure and that makes it even more complicated to create them externally. The implementation of prototype in the Mark family is not just a lifesaver for such situations but essential for the "Pattern Template" feature that we are going discuss in this section.

We want to reuse a particular stroke and make it as a "pattern template" to apply it to the CanvasView as a basis for drawing different things. The idea is to copy a particular Mark (either Stroke or Dot) and paste it on the CanvasView so the user can reuse the same pattern over and over again. A sample drawing based on that idea is shown in Figure 3-6.

A pattern with both original and cloned strokes—a black stroke indicates the original stroke. Gray strokes are copies of the black one.

Figure 3-6. A pattern with both original and cloned strokes—a black stroke indicates the original stroke. Gray strokes are copies of the black one.

The black stroke is the original stroke that was saved before as a pattern template. The gray ones are the clones of it. Both the black and gray strokes have the same shape. The cloned strokes may have different attributes in terms of color and location (or even transformation). The user can select whatever stroke onscreen and set it as a pattern template to be reused later. Each time the user applies the pattern, the application makes a copy out of the original and puts it on the CanvasView. Then the user can change the attributes, such as color, size, and location, of the stroke copies. A final structure that comprises the original stroke and the copies can be saved as another pattern template. The possibilities are unlimited.

We are not going too deep into this feature, but we will discuss the idea with some code snippets. I will leave this part for you to implement in the sample project as an exercise. Let's assume that we have an instance of Mark that was selected by the user as a pattern template called selectedMark. We will make a copy of it and save it in a data structure called templateArray, as shown in the snippet here.

id <Mark> patternTemplate = [selectedMark copy];

// save the patternTemplate in
// a data structure so it can be
// used later
[templateArray addObject:patternTemplate];

At this time, we have no idea what the user exactly selected. All we know is it's an instance of Mark aggregate that could have just one Dot or multiple Strokes and each of them has multiple Vertex in it. With just a simple yet magical copy message sent to the selectedMark, we have a perfect copy of it. Now when the user wants to apply one of the previously saved pattern templates to the CanvasView, we will need to get it from the templateArray with a patternIndex provided by the user and append it to a current Mark composite. Then the new one will become part of the current one, as shown in the following snippet.

id <Mark> patternClone = [templateArray objectAtIndex:patternIndex];
[currentMark addMark:patternClone];
[canvasView setMark:currentMark];
[canvasView setNeedsDisplay];

After we set the canvasView with an updated Mark instance, we send a setNeedsDisplay message to it to draw the Mark on the screen.

Obviously we can't add a copy of a stroke on the screen just like that without at least modifying its location; otherwise it may cover the original one if it's still there. But the code snippets should be clear enough to showcase the idea of how to use the Prototype pattern that we have implemented in Mark to make this TouchPainter app more magical.

Summary

In this chapter, we have explored how to use the Prototype pattern to implement copying for the Mark composite structure in the TouchPainter app. A deep copying implementation is necessary for a Mark aggregate that contains a tree structure when we need to make a true copy of it. An example in the Memento pattern discussed in Chapter 23 keeps a copy of a complete Mark aggregate before saving it in a memento object.

The Prototype pattern is one of the simplest and easiest patterns for object creation. In the next chapter, you will see another object creation pattern that doesn't use a copy method to create the same type of object, but a method that decides what type of object to create.



[1] The original definition appeared in Design Patterns, by the "Gang of Four" (Addison-Wesley, 1994).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.189.188.238