Chapter 12. Observer

Like an example in the Mediator pattern chapter (Chapter 11), air traffic needs a centralized air traffic control. There are many operators sitting inside the air controller tower watching their radar screens to make sure no mid-air collision can occur. At the same time, pilots who are flying their multimillion-dollar big birds need to know what's happening around them, i.e., the traffic in the air. Pilots can tune to specific channels on their radio and listen to (observe) the surrounding traffic. If a traffic controller broadcasts certain precautions and warnings to those pilots who are observing the channel, the pilots can acknowledge the messages with certain actions. So in this model, anyone can know what traffic control they are observing but not the other way around (at least the air traffic controller doesn't know all observers but certain blips on the radar screen). Anyone (including real pilots) can tune in and out whenever they want without disturbing the others.

We have brought this idea to object-oriented software design to decouple objects that have different behaviors (or to extend the behavior of an object with other different ones). With this model, different objects can work together, and at the same time they could be reused in other places as well. We call it the Observer pattern.

In this chapter, we are going to discuss the concepts of the pattern as well as how it was adapted in the Cocoa Touch framework. The Observer pattern is also part of the MVC (Model-View-Controller) pattern. We will use the TouchPainter app example from Chapter 2 to discuss how to use the pattern to let a CanvasView reflect data changes of strokes that are stored in the Model living in CanvasViewController.

What Is the Observer Pattern?

The Observer pattern is also known as the Publish-Subscribe pattern. As its a.k.a. implies, it's just like a magazine subscription. When you order a subscription from a magazine publisher, you provide the publisher your name and mailing address so the new issue can find its own way to your hand. The publisher makes sure the right magazine will be delivered to the right address. You will never (supposedly) get what you have not subscribed to. This is exactly how the Observer pattern works. An observer registers (subscribes) itself for particular notifications (magazines) with a notifier (publisher). The observer only gets what it has subscribed to when it's available from the notifier. Their static relationships are illustrated in Figure 12-1.

A class diagram of the Observer pattern

Figure 12-1. A class diagram of the Observer pattern

The Observer pattern is a publish-subscribe model. Observers subscribe to notifications from the Subject. ConcreteObservers implement an abstract Observer and override its update method. Once an instance of Subject needs to notify Observers for any new changes, the Subject will send an update message to notify any registered Observers stored in an internal list. Inside an actual implementation of the update method of a ConcreteObserver, the internal state of the Subject can be retrieved and processed later.

Note

THE OBSERVER PATTERN: Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.[10]

The whole idea of the publish-subscribe mechanism is pretty straightforward and quite easy to understand. The Subject provides methods to register and unregister any object that implements the Observer protocol and is interested in handling the update message call. When there is a change in an instance of Subject, it will send a notify message to itself. The notify method contains an algorithm that defines how to broadcast update messages to all registered observers. A common notify-update sequence at runtime is illustrated as a sequence diagram in Figure 12-2.

A sequence diagram shows the interaction between aConcreteSubject and other concrete observers that observe updates from the aConcreteSubject object.

Figure 12-2. A sequence diagram shows the interaction between aConcreteSubject and other concrete observers that observe updates from the aConcreteSubject object.

aConcreteObserver first modifies the state of aConcreteSubject. Since there is a change in its internal state, aConcreteSubject sends itself a notify message so it broadcasts update messages to a set of aConcreteObservers. aConcreteObserver and anotherConcreteObserver receive the message and send a getState message to aConcreteSubject to retrieve its internal state for further processing.

One of the most obvious benefits of using the Observer pattern is that it can extend the behavior of the Subject with N number of Observers that have specific implementation to process the information stored in the Subject. It's also a very important pattern for decoupling among different objects. The Cocoa Touch framework has provided developers some classes to use the pattern without writing their own.

When Would You Use the Observer Pattern?

You'd naturally think about using the pattern when

  • You have two types of abstraction that are dependent on each other. Encapsulating them in separate objects allows you to vary and reuse them independently.

  • A change in one object requires changing others, and the number of objects that need to be changed can vary.

  • An object needs to notify other objects without knowing what they are.

Using the Observer Pattern in Model-View-Controller

In earlier chapters, we discussed how the Model-View-Controller pattern (MVC) is a compound of different types of design patterns. Observer is one of them. A view will be associated with a controller for some particular events to occur that would affect the presentation of an application. For example, when the user clicks a "sort" button on the view, then it's passed to the controller to tell the model to sort its data in the back end. When the model successfully performs the operation on the data, it will notify all the associated controllers to update their views with the new data.

By using the Observer pattern in a MVC pattern, each component can be reused and extended independently without much interference with others in the relationships. The achieved high reusability and extensibility could not be achieved with all their logic put together in one class. So extra views can be added to a controller without modifying existing design and code. Likewise, different controllers can use the same model without changing other controllers that are using it. Especially for the model, multiple objects (either local or remote) can modify its internal data. So the model will broadcast specific changes to all observing controllers, and they will, in turn, tell their views to update the presentation with new information from the model. We will discuss more about this update mechanism in later sections.

Using the Observer Pattern in the Cocoa Touch Framework

The Cocoa Touch framework has adapted the Observer pattern with two technologies, Notifications and Key-Value Observing. Despite being two different Cocoa technologies, both of them implement the Observer pattern. We are going to discuss each of their characteristics and the differences between them.

Notifications

The Cocoa Touch framework implements the one-to-many, publish-subscribe model with NSNotificationCenter and NSNotification objects. They allow the subject and its observers to communicate in a loosely coupled manner. The communication can take place between them without either needing to know much about the other.

The subject that wants to notify other objects needs to create a notification object, which is identifiable by a global notification name, and post it to a notification center. The notification center determines the observers of a particular notification and sends the notification to them via a message. When the objects subscribe to a particular type of notification, they need to provide the name of a method identified by a selector. The method must conform to a certain single-parameter signature. The parameter of the method is the notification object, which contains the notification name, the observed object, and a dictionary containing any supplemental information. When a notification arrives, the method will be invoked.

A model object can post a notification to the notification center after the model's internal data is changed so the message can be broadcast to other observing objects and they can respond appropriately. The model can construct a notification and post it to the notification center as follows:

NSNotification *notification = [NSNotification notificationWithName:@"data changes"
                                                             object:self];
NSNotificationCenter * notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter postNotification:notification];

An instance of notification can be created by a class factory method (see the Factory Method pattern, Chapter 4) of NSNotification class with a notification name and any object passed as a parameter to observers. In the foregoing example, the notification name is @"data changes". The exact name is implementation specific. If the subject wanted to pass itself as an object parameter, self can be used for that purpose during the creation process.

Once the notification is created, it will be posted to the notification center as a parameter to a [notificationCenter postNotification:notification] message call. A reference to an instance of NSNotificationCenter can be obtained by sending a defaultCenter message to the NSNotificationCenter class. There is only one default notification center per process, which makes the default NSNotificationCenter a singleton object (see the Singleton pattern, Chapter 7). defaultCenter is a factory method that returns the sole default instance of NSNotificationCenter for the application.

Any object that wants to subscribe to the notification needs to sign itself up for it in the first place. The following code snippet illustrates that:

[notificationCenter addObserver:self
                       selector:@selector(update:)
                           name:@"data changes"
                         object:subject];

The notificationCenter is obtained the same way as in the posting steps performed by the subject. To register an observer, an observing object needs to register self as an observer in an addObserver: message call. It also needs to provide a selector that identifies a method that will be invoked when the notification center notifies the observing object. The observing object can also optionally set the name of a particular notification of interest as well as any other object as a parameter for the method that will be invoked when notified. The provided information will be used by the notification center to determine what notifications should be delivered to the observing object. For the sake of our example, at least it needs to provide the same notification name in order to be able to receive the same notification.

Key-Value Observing

Cocoa (Cocoa Touch as well) provides a mechanism called key-value observing with which objects can be notified of changes to specific properties of other objects. The mechanism is particularly important in the context of the Model-View-Controller pattern, as it enables view objects to observe changes in model objects via the controller layer.

The mechanism is based on the NSKeyValueObserving informal protocol with which Cocoa provides an automatic property-observing capability to all complying objects. For automatic observing, objects that participate in key-value observing, or KVO, require compliance with the requirements of key-value coding (KVC) and complying accessor methods. KVC is based on a related informal protocol for automatic observing by accessing object properties (for detailed specifications on key-value coding compliance, please read the "Key-Value Coding Programming Guide" available on the iOS developer's website. You can also implement manual observer notifications using the methods of NSKeyValueObserving and associated categories. With the manual implementation, you can either disable the default automatic notifications or keep both of them.

Both notifications and key-value observing are Cocoa's adaptation of the Observer pattern. Despite both relying on a similar kind of publisher-subscriber relationship, they are designed for different solutions. Table 12-1 summarizes their key differences:

Table 12-1. The Key Differences Between Notifications and Key-Value Observing

Notifications

Key-Value Observing

A central object that provides change notifications for all observers

The observed object directly transmits notifications to observers.

Mainly concerned with program events in a broad sense

Tied to the values of specific object properties

It's not rocket science to use key-value observing to activate the true spirit of the Observer pattern, especially in the context of the Model-View-Controller pattern. However, the topic itself can be a chapter of its own. If you want to know more about that, you can go the iOS developer's web site to check out its "Key-Value Observing Programming Guide," "Key-Value Coding Programming Guide," as well as "Cocoa Bindings Programming Topics."

Let's see how to use KVO to propagate notifications of changes in a model to a view (via a controller) with our old and faithful app, TouchPainter.

Updating Strokes on the CanvasView in TouchPainter

The canvas part of the TouchPainter app that allows the user to scribble is the heart and soul of the app. We briefly cover the components involved in the previous chapters. From a bird's eye view, it is basically a Model-View-Controller structure: Scribble as Model, CanvasView as View, and CanvasViewController as Controller. The CanvasView forwards touches to the CanvasViewController through a responder chain (Chain of Responsibility pattern, Chapter 17). Then the CanvasViewController processes the information of the touches and instructs Scribble to update its internal structure with the information of the new touches. Once the Scribble has updated its internal state (Mark composite; please see the Composite pattern in Chapter 13 for more details), then it will notify any observer that is registered for an update notification. In this case, the CanvasViewController is the only observer that would like to receive any updates from the Scribble. Finally the CanvasViewController will notify the CanvasView to update its presentation. Their relationships are illustrated in a diagram in Figure 12-3.

An action-flow diagram shows how CanvasView, CanvasViewController, and Scribble interact with each other.

Figure 12-3. An action-flow diagram shows how CanvasView, CanvasViewController, and Scribble interact with each other.

We will implement the solution with the Key-Value Observing mechanism, or KVO, with NSKeyValueObserving protocol. Luckily, NSObject has implemented that for us so we don't need to create everything from scratch. A class diagram that shows a possible structure is illustrated in Figure 12-4.

A class diagram shows a static relationship between Scribble and CanvasViewController through the Key-Value Observing mechanism implemented by NSObject.

Figure 12-4. A class diagram shows a static relationship between Scribble and CanvasViewController through the Key-Value Observing mechanism implemented by NSObject.

The class diagram looks a little scary, doesn't it? For the sake of brevity, other unrelated methods are omitted in the diagram. Because we are using KVO to implement the pattern and a lot of bells and whistles are hidden from view, the class diagram in Figure 12-4 doesn't quite reconcile with the one shown in Figure 12-1. But there are two key methods in NSObject shown in the figure that will ring a bell. addObserver:forKeyPath: options:context: allows an object to be added as an observer to a receiver. The observer needs to override observeValueForKeyPath:ofObject:change:context: to receive any callback from changes in the subject and process any returned value. In the previous sections, we have mentioned that the KVO update mechanism is usually automatic whenever a key-matching property is updated through its accessor method (i.e., set<key> method). If we want to manually trigger the KVO update broadcast, then we need to use willChangeValueForKey: and didChangeValueForKey: to wrap the changes before and after they occur.

Every time a different instance of Scribble is assigned to an instance of CanvasViewController, it will add itself as an observer to the instance of Scribble with the addObserver:forKeyPath:options:context: method. If there are changes to the internal state of the instance of Scribble (adding / removing components in its Mark composite object), we need to manually trigger the universal update by a pair of wrapping calls of willChangeValueForKey:@"mark" and didChangeValueForKey:@"mark". The KVO implementation in NSObject will forward a message of observeValueForKeyPath:ofObject:change:context: to the observer CanvasViewController. In the callback method, it will update its canvasView_ with a new instance of Mark from the message and ask it to redraw itself. The class declaration of Scribble is shown in Listing 12-1.

Example 12-1. A Class Declaration of Scribble in Scribble.h

#import "Mark.h"

@interface Scribble : NSObject
{
  @private
  id <Mark> parentMark_;
}

// methods for Mark management
- (void) addMark:(id <Mark>)aMark shouldAddToPreviousMark:(BOOL)shouldAddToPreviousMark;
- (void) removeMark:(id <Mark>)aMark;

@end

Scribble keeps an instance of Mark as its internal state so it won't expose to other objects. Clients can send Scribble a message of addMark:(id <Mark>)aMark shouldAddToPreviousMark:(BOOL)shouldAddToPreviousMark to insert any Mark instance to its internal composite. The BOOL parameter shouldAddToPreviousMark tells the method whether the input Mark object should be attached to the previous one as part of its aggregation (for example, a Vertex in a Stroke). removeMark:(id <Mark>)aMark allows a removal of aMark from the parent Mark.

A message of either addMark: or removeMark: for an instance of Scribble will trigger an update broadcast to its observers. Listing 12-2 shows how they are implemented.

Example 12-2. An Implemenation of Scribble in Scribble.m

#import "Scribble.h"
#import "Stroke.h"

// A private category for Scribble
// that contains a mark property available
// only to its objects
@interface Scribble ()

@property (nonatomic, retain) id <Mark> mark;

@end


@implementation Scribble

@synthesize mark=parentMark_;

- (id) init
{
  if (self = [super init])
  {
    // the parent should be a composite
    // object (i.e. Stroke)
    parentMark_ = [[Stroke alloc] init];
  }

  return self;
}

#pragma mark -
#pragma mark Methods for Mark management

- (void) addMark:(id <Mark>)aMark shouldAddToPreviousMark:(BOOL)shouldAddToPreviousMark
{
  // manual KVO invocation
  [self willChangeValueForKey:@"mark"];

  // if the flag is set to YES
  // then add this aMark to the
  // *PREVIOUS* Mark as part of an
  // aggregate.
  // Based on our design, it's supposed
  // to be the last child of the main
  // parent
  if (shouldAddToPreviousMark)
  {
    [[parentMark_ lastChild] addMark:aMark];
  }
  // otherwise attach it to the parent
  else
  {
    [parentMark_ addMark:aMark];
  }

  // manual KVO invocation
  [self didChangeValueForKey:@"mark"];
}

- (void) removeMark:(id <Mark>)aMark
{
  // do nothing if aMark is the parent
  if (aMark == parentMark_) return;

  // manual KVO invocation
  [self willChangeValueForKey:@"mark"];

  [parentMark_ removeMark:aMark];

  // manual KVO invocation
  [self didChangeValueForKey:@"mark"];
}


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

@end

@"mark" is the only key in the KVO in Scribble. Normally speaking, if we want to modify its internal mark, then we can use one of its accessor methods (e.g., setMark:). In that case, we don't need to do anything extra to broadcast an update once a modification is done as it's being handled by the default KVO mechanism defined in NSObject. We definitely need manual update because Scribble allows part-whole modifications to its mark but not just a complete replacement of it through its set method. In fact, we want both automatic and manual updates. So we will keep the original mechanism offered by NSObject and add a couple lines of code around where actual modifications to mark occur in the methods. In the addMark: method, there is an if block that tells whether the incoming aMark needs to be added to a parent or the last Mark. In either case, there will be a change to the internal mark, so we wrap the whole block with two statements: [self willChangeValueForKey:@"mark"] and [self didChangeValueForKey:@"mark"]. The first statement tells NSObject to keep the old value of the mark while the second one keeps the new one. Either one or both of the values will be passed to observers, depending on what they want to get from an update.

How should an observer handle an update? It's not the business of Scribble anymore once it hands off a whole bunch of update messages. Let's see how CanvasViewController can handle an update message to update its canvasView_ with code snippets shown in Listings 12-3 and 12-4.

Example 12-3. A Class Definition of CanvasViewController in CanvasViewController.h

#import <UIKit/UIKit.h>
#import "Scribble.h"
#import "CanvasView.h"
#import "CanvasViewGenerator.h"

@interface CanvasViewController : UIViewController
{
  @private
  Scribble *scribble_;
  CanvasView *canvasView_;

  CGPoint startPoint_;
  UIColor *strokeColor_;
  CGFloat strokeSize_;
}

@property (nonatomic, retain) CanvasView *canvasView;
@property (nonatomic, retain) Scribble *scribble;
@property (nonatomic, retain) UIColor *strokeColor;
@property (nonatomic, assign) CGFloat strokeSize;
@end

We are reusing pretty much the same CanvasViewController that appears in other chapters. Scribble is served as a model (as in Model-View-Controller) that helps store the user's stroke and dot information. A member instance of Scribble sits squarely inside a CanvasViewController object just for the purpose.

CanvasViewController also manages an instance of CanvasView as a member variable canvasView_. It uses an instance of Mark to do that actual drawing on the screen.

The implementation code for the CanvasViewController is quite long, so I will break it into multiple parts to discuss. We are going to get the setup part of the controller, as in Listing 12-4.

Example 12-4. An Implementation of CanvasViewController Defined in CanvasViewController.m

#import "CanvasViewController.h"
#import "Dot.h"
#import "Stroke.h"

@implementation CanvasViewController

@synthesize canvasView=canvasView_;
@synthesize scribble=scribble_;
@synthesize strokeColor=strokeColor_;
@synthesize strokeSize=strokeSize_;


// hook up everything with a new Scribble instance
- (void) setScribble:(Scribble *)aScribble
{
  if (scribble_ != aScribble)
  {
    [scribble_ autorelease];
    scribble_ = [aScribble retain];

    // add itself to the scribble as
    // an observer for any changes to
    // its internal state - mark
    [scribble_ addObserver:self
                forKeyPath:@"mark"
                   options:NSKeyValueObservingOptionInitial |
                           NSKeyValueObservingOptionNew
                   context:nil];
  }
}


// Implement viewDidLoad to do additional setup after loading the view,
// typically from a nib.
- (void)viewDidLoad
{
  [super viewDidLoad];

  // ...
  // Setup a default canvas view
  // but we snip that part for brevity
  // ...

  // initialize a Scribble model
  Scribble *scribble = [[[Scribble alloc] init] autorelease];
[self setScribble:scribble];

  // other setups...
}

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

Because CanvasViewController's scribble property is synthesized automatically, we normally don't need to provide any custom accessor methods for it. But here is the thing: CanvasViewController is depending on any update notifications sent from scribble_ in order to further instruct how its canvasView_ (re)draws any mark in scribble_. It needs to add itself to its private member variable scribble_ as an observer with a message call:

[scribble_ addObserver:self
            forKeyPath:@"mark"
               options:NSKeyValueObservingOptionInitial |
                       NSKeyValueObservingOptionNew
               context:nil];

The NSKeyValueObservingOptionInitial option tells scribble_ to notify CanvasViewController right after this message call to provide the initial value for its mark property. That option is important because CanvasViewController also needs to be notified when a Scribble object is being initialized in its init* method and the mark property is being set for the first time. NSKeyValueObservingOptionNew instructs scribble_ to notifiy CanvasViewController every time when the mark property of scribble_ is set with a new value. The context parameter provides an optional object as a parameter for a notification. Ok, let's get back to the message call itself. The question is, where should we put it in? If it's only in the viewDidLoad method, then when a client assigns a different Scribble instance to the controller, the observing link will be broken. So the best place to establish a link between them is in a set accessor method for scribble_. It serves as a gateway to prevent any possibility of breaking the observing link. Also, after a different Scribble reference is assigned to the CanvasViewController, the accessor method will send a message to canvasView_ to redraw itself with the mark in a new Scribble. Details on how canvasView_ draws a complete Mark composite on the screen are discussed in Chapter 15.

So instead of putting the part where we set up an observing link between CanvasViewController and its scribble_ in the viewDidLoad method, we create the first-ever instance of Scribble there every time the controller is loaded and assign it with the accessor method.

CanvasViewController and its scribble_ are now hooked up, and we are ready to see some actions on updating scribble_ with touches on the canvasView_. We put some touch event handlers in CanvasViewController, as in Listing 12-5.

Example 12-5. Touch Event Handlers in CanvasViewController

#pragma mark -
#pragma mark Touch Event Handlers

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
  startPoint_ = [[touches anyObject] locationInView:canvasView_];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
  CGPoint lastPoint = [[touches anyObject] previousLocationInView:canvasView_];

  // add a new stroke to scribble
  // if this is indeed a drag from
  // a finger
  if (CGPointEqualToPoint(lastPoint, startPoint_))
  {
    id <Mark> newStroke = [[[Stroke alloc] init] autorelease];
    [newStroke setColor:strokeColor_];
    [newStroke setSize:strokeSize_];
    [scribble_ addMark:newStroke shouldAddToPreviousMark:NO];
  }

  // add the current touch as another vertex to the
  // temp stroke
  CGPoint thisPoint = [[touches anyObject] locationInView:canvasView_];
  Vertex *vertex = [[[Vertex alloc]
                     initWithLocation:thisPoint]
                     autorelease];

  [scribble_ addMark:vertex shouldAddToPreviousMark:YES];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
  CGPoint lastPoint = [[touches anyObject] previousLocationInView:canvasView_];
  CGPoint thisPoint = [[touches anyObject] locationInView:canvasView_];

  // if the touch never moves (stays at the same spot until lifed now)
  // just add a dot to an existing stroke composite
  // otherwise add it to the temp stroke as the last vertex
  if (CGPointEqualToPoint(lastPoint, thisPoint))
  {
    Dot *singleDot = [[[Dot alloc]
                       initWithLocation:thisPoint]
                       autorelease];
    [singleDot setColor:strokeColor_];
    [singleDot setSize:strokeSize_];

    [scribble_ addMark:singleDot shouldAddToPreviousMark:NO];
  }

  // reset the start point here
  startPoint_ = CGPointZero;

  // if this is the last point of stroke
// don't bother to draw it as the user
  // won't tell the difference
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
  // we don't actually need to
  // reset the start point here
  // but just in case
  startPoint_ = CGPointZero;
}

Please see Chapters 2, 13, and 15 for detailed discussions on the requirements for the drawing part of the app.

We have a whole chunk of code for handling touch events. Our goal there is to create two different drawing scenarios. It'll be a single dot if the finger lifts off at the same spot where it first touched or a stroke (line) if it is a drag with the finger. The majority of the algorithm that deals with those conditions is in the touchesMoved: and touchesEnded: methods. In the touchesMoved: method, it will create a new instance of Stroke if it is the second point of the drag and assign it to the scribble_ with a message call, [scribble_ addMark:vertex shouldAddToPreviousMark:NO]. Further touches along the drag will be added as instances of Vertex to a previously added Mark (as a form of Stroke) with the same type of message call to scribble_. But this time the shouldAddToPreviousMark parameter is set to YES.

If it's a single touch from a finger, then touchesEnded: will create a new instance of Dot as singleDot and ask scribble_ to add it to its internal structure with shouldAddToPreviousMark set to NO.

scribble_ notifies its observers (maintained by NSObject) in every addMark: message call. We know that the CanvasViewController was hooked up with scribble_ with the addObserver: message a moment ago. But it also needs to define a callback method so when an update notification comes in from scribble_, it will do something meaningful. Part of the KVO deal requires observers to override at least one observing method. For this example, we need CanvasViewController to listen to an observeValueForKeyPath: message, as in Listing 12-6.

Example 12-6. An Implementation of updateWithScribble: Method in CanvasViewController to Handle Any Update Messages Sent from an Instance of Scribble in Case of Data Changes

#pragma mark -
#pragma mark Scribble observer method

- (void) observeValueForKeyPath:(NSString *)keyPath
                       ofObject:(id)object
                         change:(NSDictionary *)change
                        context:(void *)context
{
  if ([object isKindOfClass:[Scribble class]] &&
      [keyPath isEqualToString:@"mark"])
  {
    id <Mark> mark = [change objectForKey:NSKeyValueChangeNewKey];
    [canvasView_ setMark:mark];
[canvasView_ setNeedsDisplay];
  }
}

It first double-checks to make sure an update message is from an instance of Scribble and for its @"mark" state. If so, it will retrieve a new Mark from a change dictionary by sending it [change objectForKey:NSKeyValueChangeNewKey]. The value that is associated with NSKeyValueChangeNewKey in the dictionary will be the new value from the mark property in scribble_. After that, it tells canvasView_ to draw itself with the new Mark.

We've covered some code to show a possible way to implement the Observer pattern for the TouchPainter app. At this point, you may wonder why we needed to let the CanvasViewController update the Scribble with touch information and at the same time, expect a notification from the Scribble, and then tell the CanvasView to update itself. It seems to us there are a few extra steps for something that can be done in an easier way. If you think so, then you should go back and look at the Scribble code again. In Scribble, there are two methods that allow modifications to its internal Mark composite reference. One is to add a new Mark to a parent Mark and the other one is to remove a Mark in the parent. We already knew that the CanvasViewController would use the addMark: method to add any Dot and Stroke to the Scribble. But how about removing a Mark from it then? Who will use that one? We didn't put any code in CanvasViewController to remove a Mark in scribble_. That operation would most probably be invoked by another entity—for example, a command object (see the Command pattern, Chapter 20). An undo/redo operation can modify scribble_ without CanvasViewController's knowledge. In that case, CanvasViewController needs to know what's going on with its scribble_ behind its back. Another type of aspect that could also modify scribble_ outside the knowledge of CanvasViewController is when the model (again as in Model-View-Controller) needs to be shared with other objects just like what databases do. A database can be read and modified by objects sitting in different processes or even across the network. Changes made by one object could be vital if they are important to other objects that are supposed to be in sync but are not.

If we implemented the same thing with NSNotification and NSNotificationCenter that we have discussed in one of the previous sections instead of using KVO, then here is what's going to happen:

  • We need to define a common identifier for both the subject and observers (scribble_ and CanvasViewController).

  • When there is a change to scribble_'s internal state, then it will post a notification with the specified identifier and pass any necessary object as a parameter (as an instance of NSNotification) to NSNotificationCenter.

  • NSNotificationCenter will in turn distribute the message to all registered observers that have subscribed to any notification tagged with the identifier.

  • Then the observers will process the notification in its selector that was provided to NSNotification as a callback function.

Besides using those two approaches provided by the framework, some of you may be tempted to homebrew your own Observer infrastructure from the ground up.

Summary

In this chapter, we have covered the background information on the Observer pattern and its uses. We have also explored how we can implement it in the TouchPainter app for its Model-View-Controller architecture as a mechanism to reflect stroke changes on the screen as the user draws with a finger.

One can also take advantage of the canned implementation of the pattern with Key-Value Observing (KVO) as well as NSNotification and NSNotificationCenter objects in the Cocoa Touch framework without implementing the whole solution from scratch.

In the next part, we are going to see some design patterns that are useful for forming structures for abstract collections and other patterns that are directly related to their behaviors.



[10] 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.118.141.27