Behavioral patterns

Behavioral patterns are patterns that describe how objects will communicate with each other. In other words, it is how one object will send information to another object, even if that information is just that some event has occurred. They help to lower the code's coupling by providing a more detached communication mechanism that allows one object to send information to another, while having as little knowledge about the other object as possible. The less any type knows about the rest of the types in the code base, the less it will depend on those types. These behavior patterns also help to increase cohesion by providing straightforward and understandable ways to send the information.

This can often be the difference between doing something, such as calling your sister to ask your mom to ask your grandpa what he wants for his birthday and being able to ask your grandpa directly because you have a good communication channel open with him. In general, we will want to have the direct channel of communication open but sometimes it is actually better design to interact with fewer people, as long as we don't put too much burden on the other components. Behavioral patterns can help us with this.

The first behavioral pattern we will discuss is called the iterator pattern. We are starting with this one because we have actually already made use of this pattern in Chapter 6, Make Swift Work For You – Protocols and Generics. The idea of the iterator pattern is to provide a way to step through the contents of a container independent of the way the elements are represented inside the container.

As we saw, Swift provides us with the basics of this pattern with the GeneratorType and SequenceType protocols. It even implements those protocols for its array and dictionary containers. Even though we don't know how the elements are stored within an array or dictionary, we are still able to step through each value contained within them. Apple can easily change the way the elements are stored within them and it would not affect how we loop through the containers at all. This shows a great decoupling between our code and the container implementations.

If you remember, we were even able to create a generator for the infinite Fibonacci sequence:

struct FibonacciGenerator: GeneratorType {
    typealias Element = Int

    var values = (0, 1)

    mutating func next() -> Element? {
        self.values = (
            self.values.1,
            self.values.0 + self.values.1
        )
        return self.values.0
    }
}

The "container" doesn't even store any elements but we can still iterate through them as if it did.

The iterator pattern is a great introduction to how we make real world use of design patterns. Stepping through a list is such a common problem that Apple built the pattern directly into Swift.

The other behavioral pattern that we will discuss is called the observer pattern. The basic idea of this pattern is that you have one object that is designed to allow other objects to be notified when something occurs.

In Swift, the easiest way to achieve this is to provide a closure property on the object that you want to be observable and have that object call the closure whenever it wants to notify its observer. The property will be optional, so that any other object can set their closure on this property:

Here we have a class that represents an ATM that allows for withdrawing cash. It provides a closure property called onCashWithdrawn that is called every time cash is withdrawn. This type of closure property is usually called a callback. It is a good idea to make its purpose clear by its name. I personally choose to name all event-based callbacks by starting them with the word "on."

Now, any object can define its own closure on the callback and be notified whenever cash is withdrawn:

In this case, ATM is considered the observable object and the RecordKeeper is the observer. The ATM type is completely disconnected from whatever process might be keeping a record of its transactions. The record keeping mechanism can be changed without making any changes to the ATM and the ATM can be changed without any change to the RecordKeeper as long as the new ATM implementation still calls onCashWithDrawn whenever cash is withdrawn.

However, the RecordKeeper needs to be passed an ATM instance for this connection to be made. There can also only ever be one observer at a time. If we need to allow multiple observers, we can potentially provide an array of callbacks, but that can make removing observers more difficult. A solution that solves both of those problems is to implement the observer pattern using a notification center instead.