Over a million developers have joined DZone.

Designing Software with Swift (Part IV): Functional Type-based Design

We've covered protocol and generic design options. How does functional programming stack up? Find out.

· Mobile Zone

So far, we've looked over protocol-centric and generic type-based design. Let's expand our pallete by bringing in Swift's functional capabilities.

Functional programming has gone through it's ups and downs, no doubt. At one time, it was an esoteric programming practice confined to the AI community. Since then, some aspects of functional programming have found their way into more mainstream languages, like Swift. So, what would our observer look like using functional techniques?

The third version, using generic typing and Swift’s functional capabilities:

class Subject<K: Hashable,T> {

  var observers: [K: (T) -> Void] = [:]

  func attach(key: K, observer: (T) -> Void) {
    observers[key] = observer
  }

  func detach(key: K) {
    observers.removeValueForKey(key)
  }

  func notify(msg: T) {
    for (_, function) in observers {
      function(msg)
    }
  }
}

class GeneratingObserver {

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

  func generateClosure() -> (String) -> Void {
    return {(msg: String) -> Void in
      print("GeneratingObserver: \(msg)")
    }
  }
}

class FunctionObserver<T> {

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

  func notify(msg: T) {
    print("FunctionObserver: \(msg)")
  }
}

func observer<String>(msg: String) {
  print("observer: \(msg)")
}

var stringClosure = {(msg: String) -> Void in
  print("stringClosure: \(msg)")
}


And running this particular example is equally terse:

var subject = Subject<String, String>()
var ob1 = GeneratingObserver()
var ob2 = FunctionObserver<String>()

subject.attach(ob1.name, observer: ob1.generateClosure())
subject.attach(ob2.name, observer: ob2.notify)
subject.attach("observer", observer: observer)
subject.attach("stringClosure", observer: stringClosure)
subject.attach("anon closure") {(msg: String) -> Void in
  print("anon closure 2: \(msg)")
}


In this case, we’ve replaced our Observer object with a function, of type (T) -> Void, and we’ve specialized our generic function to the type (String) -> Void. This gives us tremendous flexibility with respect to what we submit as an observer, In our example, we use a generated function, a class method, a global function, and two different kinds of closures. All of these are compatible with the defined interface, and work just fine.

Also notice that our code has become much simpler in this final example, and we have even less of it this time than we did in Part III. Smaller amounts of simple code take less time to develop, less time to test, and less time to maintain. All this adds up to lower cost - a good thing!

So now we have three different designs for the observer design pattern. One is very protocol-centric, one does away with protocol in favor of generics, and the final one takes full advantage of Swift’s generics and functional capabilities. What are the advantages and disadvantages of each? We'll cover this in the final installment of the series.

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 }}