Observer Pattern in Swift

生来就可爱ヽ(ⅴ<●) 提交于 2019-12-09 11:58:04

问题


I want to implement an observer pattern, but I do not find the proper programming language constructs in Swift (also 2.0). The main problems are:

  1. protocol and extension does not allow stored properties.
  2. In classes we could add stored properties, but we can not force a subclass to override some of its inherited methods.

This is what I want:

{class|protocol|extension|whathaveyou} Sensor {
    var observers = Array<Any>() // This is not possible in protocol and extensions 
    // The following is does not work in classes
    func switchOn() 
    func switchOff()
    var isRunning : Bool {
        get
    }
}

class LightSensor : Sensor {
    //...
    override func switchOn() {
        // turn the sensor on
    }
}

// In the class C, implementing the protocol 'ObserverProtocol'

var lightSensor = LightSensor()
lightSensor.switchOn()
lightSensor.registerObserver(self) // This is what I want

And here comes what is possible to my knowledge:

class Sensor {
    private var observers = Array<Observer>()

    func registerObserver(observer:ObserverDelegate) {
        observers.append(observer)
    }
}

protocol SensorProtocol {
    func switchOn()
    func switchOff()
    var isRunning : Bool {
        get
    }
}

class LightSensor : Sensor, SensorProtocol {
    func switchOn() {
        //
    }
    func switchOff() {
        //
    }

    var isRunning : Bool {
        get {
            return // whatever
        }
    }
}

But this is not very convenient, because both Sensor and SensorProtocol should come hand in hand, and are both requirements the subclass LightSensor has to fulfill.

Any ideas?


回答1:


A protocol is an abstract set of requirements shared across a number of (potentially very different) other objects. As such, it's illogical to store data in a protocol. That would be like global state. I can see that you want to define the specification for how the observers are stored though. That would also allow 'you' to remove 'someone else' from being an observer, and it's very restrictive about how the observers are stored.

So, your protocol should expose methods to add and remove 'yourself' as an observer. It's then the responsibility of the object implementing the protocol to decide how and where the observers are stored and to implement the addition and removal.


You could create a struct to work with your protocols, something like:

protocol Observer: class {
    func notify(target: Any)
}

protocol Observable {
    mutating func addObserver(observer: Observer)
    mutating func removeObserver(observer: Observer)
}

struct Observation: Observable {
    var observers = [Observer]()

    mutating func addObserver(observer: Observer) {
        print("adding")
        observers.append(observer)
    }
    mutating func removeObserver(observer: Observer) {
        print("removing")
        for i in observers.indices {
            if observers[i] === observer {
                observers.removeAtIndex(i)
                break
            }
        }
    }
    func notify(target: Any) {
        print("notifying")
        for observer in observers {
            observer.notify(target)
        }
    }
}

struct ATarget: Observable {
    var observation = Observation()

    mutating func addObserver(observer: Observer) {
        observation.addObserver(observer)
    }
    mutating func removeObserver(observer: Observer) {
        observation.removeObserver(observer)
    }

    func notifyObservers() {
        observation.notify(self)
    }
}

class AnObserver: Observer {
    func notify(target: Any) {
        print("notified!")
    }
}

let myObserver = AnObserver()
var myTarget: Observable = ATarget()
myTarget.addObserver(myObserver)

if let myTarget = myTarget as? ATarget {
    myTarget.notifyObservers()
}



回答2:


This is my solution in Swift 3

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        var objectToObserve = ObjectToObserve()

        let observer = Observer()
        let observer1 = Observer()

        objectToObserve.add(observer: observer, notifyOnRegister: true)
        objectToObserve.add(observer: observer1, notifyOnRegister: true)
    }
}

//
// MARK: Protocol
//
protocol Observing: class {
    func doSomething()
    func doSomethingClosure(completion: () -> Void)
}

protocol Observable {

}

extension Observable {

    private var observers: [Observing] {
        get {
            return [Observing]()
        }
        set {
            //Do nothing
        }
    }

    mutating func add(observer: Observing, notifyOnRegister: Bool) {
        if !observers.contains(where: { $0 === observer }) {
            observers.append(observer)

            if notifyOnRegister {
                observer.doSomething()
                observer.doSomethingClosure(completion: {
                    print("Completion")
                })
            }
        }
    }

    mutating func remove(observer: Observing) {
        observers = observers.filter({ $0 !== observer })
    }
}

//
// MARK: Observing
//
class ObjectToObserve: Observable {

    init() {
        print("Init ObjectToObserve")
    }
}

class Observer: Observing {

    init() {
        print("Init Observer")
    }

    func doSomething() {
        print("Do something")
    }

    func doSomethingClosure(completion: () -> Void) {
        print("Do something Closure")
        completion()
    }
}



回答3:


All answers above incorrectly use an array for retaining the observers, which may create retain cycles because of the strong references.

Also in general you may not want to allow the same observer to register itself twice.

The presented solutions also are not general purpose or lack type safety. I reference my blog post here which presents a full solution in a Swifty manner:

https://www.behindmedia.com/2017/12/23/implementing-the-observer-pattern-in-swift/




回答4:


Well, you can certainly overcome the restriction of not having stored properties on extensions. Maybe that way you can complement one of the proposed solutions with an extension that helps you avoid creating the observer list in each subclass / protocol implementation.

Although extensions can't have stored properties, you can actually get them by using the Objective-C Runtime. Assuming you have a base class for your sensors (BaseSensor) and a protocol for observers (SensorObserver):

import Foundation
import ObjectiveC

private var MyObserverListKey: UInt8 = 0

extension BaseSensor {
  var observers:[SensorObserver] {
    get {
      if let observers = objc_getAssociatedObject( self, &MyObserverListKey ) as? [SensorObserver] {
        return observers
      }
      else {
        var observers = [SensorObserver]()
        objc_setAssociatedObject( self, &MyObserverListKey, observers, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC) )
        return observers
      }
    }
    set(value) {
      objc_setAssociatedObject( self, &MyObserverListKey, observers, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC) )
    }
  }
}

To be clear, even though you would need BaseSensor and all Sensors to inherit from it in order to have this property, BaseSensor wouldn't actually implement the Sensor protocol. It's a bit weird, but I think it would suit your needs:

class BaseSensor {
}

protocol Sensor {
   func switchOn()
}

class LightSensor: BaseSensor, Sensor {
   func switchOn() {
     // whatever
   }
}

With Swift 2.0 this would be much simpler, since you can use Protocol Extensions, so you could simply do this:

protocol Sensor {
  func switchOn()
}

extension Sensor {
  // Here the code from the previous implementation of the extension of BaseSensor
}

class LightSensor : Sensor {
   func switchOn() {
     // whatever
   }
}

Way better.



来源:https://stackoverflow.com/questions/30976995/observer-pattern-in-swift

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!