This chapter presents you with three other behavioral patterns, which support communication between objects. Objects keep their independence and sometimes their anonymity. The iterator pattern is often used with array, collection, and dictionary objects. The mediator allows communication between two objects without knowing each other's identities and the observer patterns mirror the publish/subscribe methodologies that are well known in distributed systems.
This chapter is divided in three sections:
This pattern is commonly used in many languages with an array or a collection of objects. It allows iteration over a list of objects contained in a collection.
The iterator pattern allows you to iterate sequentially over an aggregated object of objects without having to know how the collection is structured.
Here, you'll find the generic UML class diagram of the pattern, but note that we will not implement it using this way.
Indeed, Swift provides some types that will simplify the implementation of the iterator pattern, without having to produce all of the needed requirements by hand.
Why reinvent the wheel? The following figure represents the generic UML class diagram:
As with every pattern described up to now, I will tell you what the participants in this pattern are even if we will not see all of them in our implementation of the pattern:
Collection
: This is an abstract class that implements the association of the collection with items and the CreateIterator()
methodConcreteCollection
: This is concrete collection subclass that link the CurrentItem
object to a ConcreteItem
object and the Iterator
interface to the ConcreteIterator
object
Iterator
: This is the abstract class, which implements the association of the iterator and the collection items and methodsConcreteIterator
: This is a concrete subclass that links our currentItem
to the ConcreteItem
objectItem
: This is the abstract class of the collection itemsConcreteItem
: This is a concrete Item
subclass used by ConcreteIterator
and ConcreteCollection
The iterator keeps in memory the current item in the collection. It can also calculate and predict the next object of the iteration.
You are developing a game in which you can have up to say, four players. You want to be able to iterate over all the four players to do something. In our sample, we will display the name of each player.
The implementation provided here comes from ideas discovered on the Lilly Labs website at http://lillylabs.no/2014/09/30/make-iterable-swift-collection-type-sequencetype/.
Well, given our illustration what we want to be able to do is to iterate using a for…in
loop construct.
All thanks to Swift, we have some elements that will simplify the implementation of this pattern.
Indeed, Swift proposes a SequenceType
protocol and an AnyGeneratorType<T>
class, which implements the GeneratorType
protocol.
The SequenceType
protocol defines a protocol that allows us to iterate over elements of a collection using the for…in
loop construct. It requires that the class implements the generate()
method, which returns an object conforming to the GeneratorType
protocol.
AnyGenerator<T>
is a class that conforms to the GeneratorType
protocol, where <T>
means Item
of any type.
Having said this, how do we use all of the preceding functions to easily iterate over a collection of any type? Let's say, we have the following class:
class Player { var name: String! init(name: String) { self.name = name } }
Therefore, we define a simple Player
class where we pass a string in the constructor, which corresponds to the name of the player.
We suppose that we have four players in our game and want to be able to iterate over each of them to display their name.
Therefore, the final code to test will be something like the following:
for player in players { print("analysing (player.name)") }
Now, how to complete our code with a bonus, something that will works with any class that we want to iterate over? Well, we will use another concept provided by Swift: extension.
The first thing to be done is to create an object or struct that we will let us iterate over any class type:
struct OurCollection<T> { let items: [T] }
Therefore, we define a struct that we call OurCollection
, where items are of type T
.
Now, we will be able to write the following:
let player1 = Player(name: "Helmi") let player2 = Player(name: "Raphael") let player3 = Player(name: "Adrien") let player4 = Player(name: "Alain") let players = OurCollection(items:[player1,player2, player3, player4])
However, the for…in
loop will still not work, as shown in the following screenshot:
Even though, the players don't implement the SequenceType
protocol. Here is the magic:
extension OurCollection: SequenceType { typealias Generator = AnyGenerator<T> func generate() -> Generator { var i = 0 return anyGenerator { return i >= self.items.count ? nil : self.items[i++] } } }
Wow! Lot of new things here:
First, we create an extension of the OurCollection
struct by telling that we want to implement the SequenceType
protocol.
So we implement the generate()
method, which will return the next type T
object in the iteration. Also, note the line:
func generate() -> Generator {
Generator is an alias of AnyGenerator<T>
:
typealias Generator = AnyGenerator<T>
We use this to simplify the writing. We can remove the type alias statement and write:
func generate() -> AnyGenerator<T> {
Another function is the anyGenerator
function tha you see here:
return anyGenerator { return i >= self.items.count ? nil : self.items[i++] }
The Swift 2.0 documentation says that the anyGenerator
function has the following signature:
func anyGenerator<Element>(body: () -> Element?) -> AnyGenerator<Element>
The purpose of this function is to return a GeneratorType
instance whose next
method invokes body
and returns the result.
So here, we start from index 0
to index self.items.count
value and add the sel.items[i++]
in the new GeneratorType
instance. The new GeneratorType
instance is returned when i
is superior to a number of elements in the items array.
We can also write the function such as:
func generate() -> Generator { var i = 0 let seq = anyGenerator {i < self.items.count ? self.items[i++] : nil} return seq }
And also like this:
func generate() -> Generator { var i = 0 return anyGenerator {i < self.items.count ? self.items[i++] : nil} }
Here, we use a closure with the anyGenerator
function to return a new sequence of elements that we can iterate over. Our final code is as follows:
import Foundation struct OurCollection<T> { let items: [T] } class Player { var name: String! init(name: String) { self.name = name } } extension OurCollection: SequenceType { typealias Generator = AnyGenerator<T> func generate() -> Generator{ var i = 0 // Construct a AnyGenerator<T> instance, passing a closure // that returns the next type T object in the iteration return anyGenerator { return i >= self.items.count ? nil : self.items[i++] } } } let player1 = Player(name: "Helmi") let player2 = Player(name: "Raphael") let player3 = Player(name: "Adrien") let player4 = Player(name: "Alain") let players = OurCollection(items:[player1,player2, player3, player4]) for player in players { print("Name: (player.name)") }
Open the iteratorPattern
project, build and run it. You will now see the following result: