Design patterns for iOS developers. Observer Part I

Instead of the foreword


It's been 17 years since the legendary book of the Gang of Four, dedicated to Design patterns, was released. Despite such a solid period, it is difficult to challenge the relevance of the methods described in it. Design patterns live and evolve. They are used, discussed, scolded and praised. Unfortunately, for many they still remain an unnecessary abstraction.

Discussing various programming issues with colleagues both in life and on various resources, it is often necessary to explain the importance of a particular pattern. And the idea was born to show concrete examples of how their use can make life easier for the programmer. Even when it comes to a platform like iOS.


Observer Pattern


Life example

Student Vasya loves to go to parties. To be more precise, they occupied such an important part of his life that he was expelled from the institute. Having started looking for work, Vasya realized that he knew almost nothing and could not. Although, wait ... Vasya has a lot of beautiful girls who do not feed bread - let them get into a cool party without an invitation. Vasya is also known in all the relevant institutions of his city. Finally, Vasya understands: for the party to succeed (wealthy visitors spent a lot of money), it would be nice to fill it with beautiful girls (who will spend this money on).

So Vasya became a pimpopened his own business. The owners of the institutions turn to Vasya (who doesn’t know Vasya ?!) with news about fashionable private events. Vasya tells his girlfriends that he can take them there. Where does he get the girls from? It's simple: take the Light. She met Vasya at the club last week. Sveta had long dreamed of visiting an elite party, so she asked Vasya to write down her phone number. She is a pretty girl, very well dressed, so Vasya agreed. So Light signed on Vasin services and become an observer . However, Vasya warned her that he would call when the time came.

A year passed, Sveta graduated from the institute, found a job and, most importantly, got married! She no longer wants to go to parties, so yesterday she called Vasya and asked her not to annoy her anymore with her idiotic messages. So she unsubscribed from the (very suspicious) subject of Vasya. However, she knows that she can always sign up again (she is still pretty, young, well dressed).

Meanwhile, Vasya did not stand still, but expanded his business. Recently, he met several very wealthy football players who like to relax at a party and are always ready to treat pretty girls with a non-alcoholic cocktail. Naturally, they can also be invited to various events (the organizers are delighted, they pay Vasya more money). Business expansion went unnoticed: really, who cares who to call? Both girls and football players have telephones. Sometimes, reporting on a new event, Vasya even confuses whom he is talking to.

Shorter example

Those who closely followed Vasya’s business could immediately recall the newsletters. Indeed, we can leave your e-mail address on your favorite resource and receive spam important and interesting news without having to visit the corresponding web page every day. As in the case of Sveta, we always (in fact, with any luck) can unsubscribe from the newsletter.

Definition

Gang of Four

  • Title: Observer.
  • Classification: Behavior Pattern.
  • Appointment:
    Defines a one-to-many relationship between objects in such a way that when a state of one object changes, all dependent on it are notified about it and are automatically updated.


Comments

So, the observer pattern defines a one-to-many relationship. Moreover, an object that reports about its changes is called a subject , and those objects to which it reports about them are called observers .

It is worth noting an important feature of the observer pattern: the subject may know almost nothing about the observers. So, Vasya did not see any difference between the girls and the players.

Structure

image

Observer for iOS Developer


Let's move on to programming. First, we will analyze our own implementation of the observer pattern using a specific example, and then we will analyze the notification mechanism implemented in Cocoa.

Own implementation

Let's say we are developing a completely new game that will certainly blow up the App Store. After passing the next level, we need to do two things:
  1. Show congratulatory screen.
  2. Open access to new levels.

It is worth noting that the first and second actions are not related to each other. We can execute them in random order. We also suspect that in the near future it will be necessary to expand the number of such actions (for example, we want to send some data to our server or check if the user has earned an achievement in the Game Center).

We apply the studied pattern of the observer. Let's start with the observer protocol.

@protocol GameStateObserver 
 
- (void) completedLevel: (Level *) level withScore: (NSUInteger) score;
 
@end


Everything is pretty transparent here: observers will implement the protocol GameStateObserver. We will deal with the subject.

@protocol GameStateSubject 
 
- (void) addObserver: (id) observer;
- (void) removeObserver: (id) observer;
- (void) notifyObservers;
 
@end


Let's move on to the classes that interest us. Let the state of the current game be stored in the class object GameState. Then its definition will look something like this:

@interface GameState: NSObject  {
    ...
    NSMutableSet * observerCollection;
}
 
@property (readonly) Level * level;
@property (readonly) NSUInteger score;
 
...
- (void) updateState;
 
@end


At the same time, we believe that the method updateStateis called every time significant changes occur in the game. Here is a part of the implementation GameState:

@implementation GameState
 
...
- (void) addObserver: (id) observer {
    [observerCollection addObject: observer];
}
 
- (void) removeObserver: (id) observer {
    [observerCollection removeObject: observer];
}
 
- (void) notifyObservers {
    for (id observer in observerCollection) {
        [observer completedLevel: self.level withScore: self.score];
    }
}
 
- (void) updateState {
    ...
    if (levelCompleted) {
        [self notifyObservers];
    }
}
 
@end


Now, for any object that needs to know about the successful completion of the level, it is enough to implement the protocol GameStateObserverand subscribe to the notification of successful completion. The appropriate code would look something like this:

GameState * gameState = [[GameState alloc] init];
[gameState addObserver: levelManager];
[gameState addObserver: levelViewController];


Here levelViewControlleris the controller responsible for the interface of the game process, and levelManageris the model object corresponding to the levels.

Discussion

We examined a rather primitive example, which, however, is ubiquitous. It’s immediately obvious that the solution is quite flexible. It is worth noting that in the notification we decided to transfer some data as parameters. Such an implementation has its pros and cons. It may be convenient to use the following protocol variant GameStateObserver:

@protocol GameStateObserver 
 
- (void) levelCompleted: (id) subject;
 
@end


The corresponding option GameStateSubjectwould look like this:

@protocol GameStateSubject 
 
- (void) addObserver: (id) observer;
- (void) removeObserver: (id) observer;
- (void) notifyObservers;
 
@property (readonly) Level * level;
@property (readonly) NSUInteger score;
 
@end


Observer at Cocoa: Notifications

It turns out that Cocoa has a mechanism to implement an observer pattern. To be completely precise, there are two such mechanisms. At the moment, we will focus on the notification mechanism, and leave the second for the future. Further, we will not go into all the subtleties, but only describe the basic functionality.

Informally, the notification mechanism allows you to do two things: subscribe / unsubscribe from the notification and send the notification to all subscribers. A notification is an instance of a class NSNotification. Alerts are specified by their string nametype name NSString. In addition to the name, the alert also contains the subject objectand additional userInfotype data NSDictionary.

For subscriptions and delivery of notifications is responsibleNSNotificationCenter. To access it, in most cases, just call the class method defaultCenter.

To subscribe to a message, the alert center has a method addObserver:selector:name:object:. The first parameter is the observer, the second is the selector, which will be called upon notification. It must have a signature - (void)methodName:(NSNotification *). The third parameter is the name of the alert, the fourth is the subject. If you transfer as an entity nil, then the notification will be delivered from an arbitrary sender (provided that the name matches). Using alerts, the subscription code would look like this:

GameState * gameState = [[GameState alloc] init];
[[NSNotificationCenter defaultCenter] addObserver: levelManager
    selector: @selector (levelCompleted :)
    name: @ "LevelCompletedNotification" object: gameState];
[[NSNotificationCenter defaultCenter] addObserver: levelViewController
    selector: @selector (levelCompleted :)
    name: @ "LevelCompletedNotification" object: gameState];


Approximate type of method levelCompleted:

- (void) levelCompleted: (NSNotification *) notification {
    id subject = [notification object];
    Level * level = subject.level;
    NSUInteger score = subject.score;
}


Generally speaking, the protocol GameStateSubjectcan be disposed of and used directly GameState.

To unsubscribe from alerts, you need to call one of the methods removeObserver:or removeObserver:name:object:.

Sending an alert is an even simpler process. There are methods postNotificationName:object:and postNotificationName:object:userInfo:. The first sets the value to userInfozero nil. A new implementation of the method notifyObserversfollows.

- (void) notifyObservers {
    [[NSNotificationCenter defaultCenter]
        postNotificationName: @ "LevelCompletedNotification" object: self];
}


Comments

The mechanism for sending alerts can do much more than we described. For example, all of the above code is synchronous. To send alerts asynchronously, you need to use alert queues NSNotificationQueue.

It's also worth noting that everything described works for both Cocoa and Cocoa Touch. In the future, we will still use some features of the iOS platform.

Instead of an afterword


Let's summarize what we have reviewed and learned. So we:
  • studied the design pattern of the observer ;
  • used his own implementation in a frequently encountered situation;
  • Learned to do the same with Cocoa's notification engine.
  • learned that design patterns are not scary, not complicated and not cumbersome;
  • realized that the patterns are not taken from the ceiling, but from real life (remember the pimp of promoter Vasya)!

In the second part, we will learn about the Key-Value Observing mechanism, which also implements the observer pattern. There are many more patterns ahead!

Useful sources


  • E. Gamma, R. Helm, R. Johnson, J. Vlissides Receptions of object-oriented design. Design Patterns = Design Patterns: Elements of Reusable Object-Oriented Software. - St. Petersburg: Peter, 2007. - S. 366. - ISBN 978-5-469-01136-1 (also ISBN 5-272-00355-1)
  • E. Freeman, E. Freeman, C. Sierra, B. Bates Design Patterns = Head First Design Patterns - St. Petersburg: Peter, 2011. - P. 656. - ISBN 978-5-459-00435-9
  • Notification Programming Topics - iOS Developer Library

Also popular now: