Skip to content

Instantly share code, notes, and snippets.

@ethereal-engineer
Created December 19, 2015 02:17
Show Gist options
  • Save ethereal-engineer/44ce645b5c9fbfb45a96 to your computer and use it in GitHub Desktop.
Save ethereal-engineer/44ce645b5c9fbfb45a96 to your computer and use it in GitHub Desktop.
WIP Observer Pattern Mental Workthrough
//: Observeground - noun: a place where things can watch
import UIKit
var str = "Hello, observers"
/// Generic protocol
protocol Observable {
typealias ObserverType
var observers: [ObserverType] { get set }
mutating func addObserver(observer: ObserverType)
mutating func removeObserver(observer: ObserverType)
func observersDidChange(isEmpty: Bool)
}
/// Generic implementation
extension Observable {
mutating func addObserver(observer: ObserverType) {
observers.append(observer)
observersDidChange(false)
}
}
/// Implementation for reference type observers
extension Observable where ObserverType : AnyObject {
mutating func removeObserver(observer: ObserverType) {
// If the observing collection doesn't contain the observer then bail
guard let index = observers.indexOf({$0 === observer}) else {
return
}
observers.removeAtIndex(index)
observersDidChange(observers.count > 0)
}
}
/// Implementation for value type observers
extension Observable where ObserverType : Equatable {
mutating func removeObserver(observer: ObserverType) {
// If the observing collection doesn't contain the observer then bail
guard let index = observers.indexOf({$0 == observer}) else {
return
}
observers.removeAtIndex(index)
observersDidChange(observers.count > 0)
}
}
/// Example entity protocols to observe (extracted to allow proper unit testing)
protocol Kettle {
var isOn: Bool { get }
var isBoiled: Bool { get }
mutating func turnOn()
mutating func turnOff()
mutating func comeToBoil()
}
protocol Rain {
var isFalling: Bool { get }
mutating func startFalling()
mutating func stopFalling()
}
/// Observer delegate-style protocols
// Every observer of Kettle agrees to implement this protocol
protocol KettleObserver {
func kettle(kettle: Kettle, didChangeOnState isOn: Bool)
func kettleDidComeToBoil(kettle: Kettle)
}
// Every observer of Rain agrees to implement this protocol
protocol RainObserver {
func rain(rain: Rain, didChangeFallingState isFalling: Bool)
}
/// Extend observee protocols to allow observing
/// Concrete Value Type Test Types
struct TestKettleValueType {
var isOn: Bool = false
var isBoiled: Bool = false
mutating func turnOn() {
isOn = true
}
mutating func turnOff() {
isOn = false
}
mutating func comeToBoil() {
isBoiled = true
}
}
extension TestKettleValueType : Kettle {}
struct TestRainValueType {
var isFalling: Bool = false
mutating func startFalling() {
isFalling = true
}
mutating func stopFalling() {
isFalling = false
}
}
extension TestRainValueType : Rain {}
/// Concrete Reference Type Test Types
class TestKettleReferenceType {
var isOn: Bool = false
var isBoiled: Bool = false
func turnOn() {
isOn = true
}
func turnOff() {
isOn = false
}
func comeToBoil() {
isBoiled = true
}
}
extension TestKettleReferenceType : Kettle {}
class TestRainReferenceType {
var isFalling: Bool = false
func startFalling() {
isFalling = true
}
func stopFalling() {
isFalling = false
}
}
extension TestRainReferenceType : Rain {}
/// Concrete observer examples
/// Reference type examples
class KettleObserverReferenceType {
let kettle: Kettle
init(kettle: Kettle) {
self.kettle = kettle
}
func startObserving() {
}
func stopObserving() {
}
}
extension KettleObserverReferenceType : KettleObserver {
func kettle(kettle: Kettle, didChangeOnState isOn: Bool) {
print("Kettle \(kettle) did change on state to \(isOn).")
}
func kettleDidComeToBoil(kettle: Kettle) {
print("Kettle \(kettle) did come to boil.")
}
}
/// Value type examples
struct KettleObserverValueType {
let kettle: Kettle
init(kettle: Kettle) {
self.kettle = kettle
}
func startObserving() {
kettle.addObserver(self)
}
func stopObserving() {
kettle.removeObserver(self)
}
}
extension KettleObserverValueType : KettleObserver {
func kettle(kettle: Kettle, didChangeOnState isOn: Bool) {
print("Kettle \(kettle) did change on state to \(isOn).")
print("Our copy of Kettle \(self.kettle) has on state \(kettle.isOn).")
}
func kettleDidComeToBoil(kettle: Kettle) {
print("Kettle \(kettle) did come to boil.")
print("Our copy of Kettle \(self.kettle) has boil state \(kettle.isBoiled).")
}
}
/// Live testing
// Test #1: Reference type observers and observees
var testKettle = TestKettleReferenceType()
let kettleObserver = KettleObserverReferenceType(kettle: testKettle)
kettleObserver.startObserving()
testKettle.turnOn()
testKettle.comeToBoil()
testKettle.turnOff()
kettleObserver.stopObserving()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment