The observer pattern

The observer pattern is another behavioral pattern that is often used in networked system where a subject (the server) will notify some client. The iOS makes large use of this pattern through NSNotificationCenter object.

Roles

The observer pattern creates dependence between a subject and observer so that the observer is notified in order to update their state each time the subject is modified.

This composition means that observer does not need to ask the current state of the subject. They only need to register to its notifications.

This pattern can be used when:

  • A state modification inside the object needs to update other objects dynamically
  • An object wants to prevent other objects without the need to know the type (without having to be high coupled with them)
  • We do not want to merge two objects into one

Design

The following diagram represents the UML class diagram of the observer pattern:

Design

Participants

This pattern is composed of the following participants:

  • Subject: This defines the methods needed to add, remove, and notify observers.
  • ConcreteSubject: This implements the Subject methods. It sends a notification when its state is modified.
  • Observer: This is a common interface having an update() method, which will be invoked by the subject when the observer needs to be notified about the modification of the subject.
  • ConcreteObserver1 and ConcreteObserver2: This implements the update() method.

Collaboration

The ConcreteSubject class notifies the observers when its internal state is modified. When a concrete observer receives this notification, it is updated consequently. To complete the update, it can invoke some subject methods that give access to its state.

Illustration

You are working on a new website where you want to allow internet users to communicate each other through a chat system. Your first job will be to provide a room, the entry point of all Internet users. Each time a new user joins the room, every user is notified.

The observer pattern is fully appropriated to implement the code, which solves this problem.

Implementation

Open the ObserverPattern Xcode project to see the current structure of our code:

Implementation

We will retrieve the Subject folder and Observers folder, where we will find the participants of our pattern. The Helpers folder contains a class that we will use later when sending a message.

The Extension folder contains an array extension that is required to make it possible for us to remove a particular object from the collection of users managed by the subject.

Lastly, we find the main.swift file used to simulate interactions.

So, let us begin our code by defining our observer in the UserProtocol.Swift file:

protocol UserProtocol {
  func update(object:AnyObject)
}

We simply define an update method with an object as an argument. The implementation of the UserProtocol will be like this:

class User: UserProtocol{
  let name: String!
  
  init(name: String) {
    self.name = name
  }
  
  func update(object:AnyObject) {
    let info = object as! Info
    print("(self.name) notified that (info.message) have status (info.status) on (info.date.description)")
  }
}

We pass a name in the constructor of the User object.

Then, in the update method, we prepare a message that will be displayed on the console. We downcast our object of type AnyObject to an Info object; this object is a helper. You will find its code in the Helper folder in the Info.swift file:

class Info {
  var date = NSDate()
  var message:String!
  var status:InfoStatus!
  
  init(msg: String, status:InfoStatus) {
    self.message = msg
    self.status = status
  }
}

The Info object contains three values: a date, message, and status.

The date is the current date and is defined when the Info object is initialized. The message is a string received in argument during initialization of the info object and the status is an enumeration passed in an argument during the initialization of the object and it can have the following value:

enum InfoStatus {
  case Join
  case Leave
}

Now, we only have to define our Subject protocol, and implement it in our concrete Subject. The Subject represents the object that need to be observed.

Our Subject definition is available in the RoomProtocol.swift file in the Subject folder:

protocol RoomProtocol {
  func addObserver(user: User)
  func removeObserver(user: User)
  func notifyObserver(object: AnyObject)
}

These three methods are the minimum necessary to any subject in an observer pattern.

The addObserver function lets you register an observer in the collection of observers managed by the subject.

The removeObserver method is used to remove an observer from the collection managed by the subject.

Last, the notifyObserver method is used to notify all our observers.

The implementation will be found in the Room.swift file, as shown:

class Room: RoomProtocol {
  
  private var users = [User]()
  
  func addObserver(user: User) {
    users.append(user)
    let info = Info(msg: "(user.name)", status: .Join)
    notifyObserver(info)
  }
  
  func removeObserver(user: User) {
    users.removeObject(user)
    let info = Info(msg: "(user.name)", status: .Leave)
    notifyObserver(info)
  }
  
  func notifyObserver(object: AnyObject){
    for u in users {
      u.update(object)
    }
  }
}

Here, you retrieve the three methods, in the two first one; you see a call to the notifyObserver method.

All the users will be notified each time the addObserver or removeObserver method is called because the addObserver method is called when a new user joins the room and we display the Join status in the Info message. With the same principle, we display the Leave status when the removeObserver method is called.

The notifiyObserver method receives an object of type AnyObject as an argument that will be propagated over the update method of each user object available in the collection managed by the Room method.

Time for us to now write our demo code, open the main.swift file, and write the code.

First, we initialize our Room method and four internet users:

let room = Room()
let user1 = User(name:"Julien")
let user2 = User(name:"Alain")
let user3 = User(name:"Helmi")
let user4 = User(name:"Raphael")

Then, we register each user to the room:

room.addObserver(user1)
room.addObserver(user2)
room.addObserver(user3)
room.addObserver(user4)

Note

Each time the addObserver method is called, all the currently registered users will be notified that the current registered user has joined.

So, when room.addObserver(user1) is called, only user1 will be notified, but when user2 is registered, user1 and user2 will be notified and so on.

Now, we remove user2, user3, and user1 in this order:

room.removeObserver(user2)
room.removeObserver(user3)
room.removeObserver(user1)

To complete our sample, we register user2 once more:

room.addObserver(user2)

Let us now build and run the project. You will get the following result:

Implementation

Here, we see all the notifications. The first line corresponds to the first addObserver called. The next two lines correspond to the second addObserver called, and so on.

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

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