Created
December 19, 2015 02:17
-
-
Save ethereal-engineer/44ce645b5c9fbfb45a96 to your computer and use it in GitHub Desktop.
WIP Observer Pattern Mental Workthrough
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //: 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