Over a million developers have joined DZone.

Designing Software with Swift (Part II): Protocol-based Design

In the second installment of this series, let's continue with good-old fashioned protocol-based design.

· Mobile Zone

Let’s take a look at dynamic polymorphism and runtime binding. First, let’s define some simple protocols. In this and upcoming examples, we’ll borrow the original method, class, and interface naming from the original GoF design patterns book.

public protocol Observer {
typealias DataType
func update(data: DataType)
}


The first, the Observer protocol, defines the notification interface. Supporting classes receive an update via the update(_:) method. In the original GoF version, the update method doesn’t accept any data. Ours does.

public protocol Subject {
    typealias KeyType
    typealias ObserverType
    mutating func attach(key: KeyType, observer: ObserverType)
    mutating func detach(key: KeyType)
}


The next protocol defines the interface for adding observers. We define a few typealiases for the protocol, and we do change the signature a bit from the original in the signature of attach(_:observer:) and detach(_:).

Now, let’s look at the implementations:

public class ConcreteObserver : Observer {

    let name : String = String(arc4random_uniform(10000))

    public func update(data: String) {
  print("updated : \(data)")
    }
}


Nice and simple observer, right? it has a randomly generated name, used as a key on the observer when stored. When the update(_:) method is called, it prints updated , the new data, and exits.

public class ConcreteSubject<T: Observer, S: Equatable where S == T.DataType> : Subject {

  private var observers: [String: T] = [:]

  private var data : S?

  var state: S? {
    get {
    return data
  }
  set(data) {
    self.data = data
    notify()
  }
  }

  private func notify() {
    for (_, o) in observers {
      if let mystate = state {
      o.update(mystate)
   }
  }
  }

  public func attach(key: String, observer: T) {
    observers[key] = (observer)
  }

  public func detach(key: String) {
    observers.removeValueForKey(key)
  }
}


The ConcreteSubject class implements the Subject protocol, implementing the various ways to register an Observer (e.g. attach(_:) and detach(_:), a private method for notifying observers, and a property that notifies observers via that private method whenever state changes. We store observers in a dictionary indexed by observer name, and the class is genericized by <T>, which must implement the Observer protocol, and <S>, which must be the same DataType as the type used in the observer interface.

We run the code, and it outputs updated with the new text state, as expected:

let observer = ConcreteObserver()
let subject = ConcreteSubject<ConcreteObserver, String>()
subject.attach(observer.name, observer: observer)
subject.state = "some new data"


Perfectly sensible, protocol-oriented, with generic types used where appropriate. And it works, always a plus! Let’s shake things up a bit by desiging this completely differently. See how in the next installment.

Topics:
swift ,design ,pattern

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}