Swift中的观察者模式

时间:2015-06-22 09:55:19

标签: swift interface observer-pattern

我想实现一个观察者模式,但是我没有在Swift(也是2.0)中找到合适的编程语言结构。主要问题是:

  1. protocolextension不允许存储的属性。
  2. 在类中,我们可以添加存储的属性,但是我们不能强制子类覆盖它的一些继承的方法。
  3. 这就是我想要的:

    {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
    

    这就是我所知道的可能性:

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

    但这不是很方便,因为SensorSensorProtocol都应该齐头并进,并且都是子类LightSensor必须满足的要求。

    有什么想法吗?

4 个答案:

答案 0 :(得分:5)

协议是在许多(可能非常不同的)其他对象之间共享的一组抽象需求。因此,将数据存储在协议中是不合逻辑的。这就像全球国家。我可以看到你想要定义观察者如何存储的规范。这也可以让你“#”;删除其他人&#39;作为一个观察者,它对观察者的存储方式有着非常严格的限制。

因此,您的协议应该公开添加和删除自己的方法。作为观察者。然后,实施协议的对象有责任决定观察者的存储方式和位置,并实施添加和删除。

您可以创建一个结构来使用您的协议,例如:

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

答案 1 :(得分:1)

这是我在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()
    }
}

答案 2 :(得分:0)

嗯,你当然可以克服在扩展上没有存储属性的限制。 也许这样你可以用一个扩展来补充一个提议的解决方案,这个扩展可以帮助你避免在每个子类/协议实现中创建观察者列表。

虽然扩展不能有存储属性,但实际上可以通过使用Objective-C Runtime来获取它们。假设您有传感器的基类(BaseSensor)和观察者协议(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) )
    }
  }
}

要明确的是,即使您需要BaseSensor和所有传感器继承它以获得此属性,BaseSensor也不会实际实现传感器协议。 这有点奇怪,但我认为它符合您的需求:

class BaseSensor {
}

protocol Sensor {
   func switchOn()
}

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

使用Swift 2.0,这会更简单,因为你可以使用Protocol Extensions,所以你可以这样做:

protocol Sensor {
  func switchOn()
}

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

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

更好的方式。

答案 3 :(得分:0)

上面的所有答案都错误地使用数组来保留观察者,由于强引用,这可能会创建保留周期。

通常,您可能不希望允许同一个观察者自己注册两次。

所提出的解决方案也不是通用的或缺乏类型安全性。我在这里引用我的博客文章,以一种Swifty的方式呈现完整的解决方案:

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