【问题标题】:Observer Pattern in SwiftSwift 中的观察者模式
【发布时间】:2015-09-07 17:31:22
【问题描述】:

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

  1. protocolextension 不允许存储属性。
  2. 我们可以在类中添加存储属性,但不能强制子类覆盖其继承的某些方法。

这就是我想要的:

{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 必须满足的要求。

有什么想法吗?

【问题讨论】:

  • 这可能是不可能的,因为你不能在扩展中创建变量,对吧?就我而言,这适用于普通课程
  • 是的,但正如我所写,在一个类中我不能强制执行需要实现的方法!
  • 我更新了演示代码,明确我想要什么,什么不可能。
  • 您可能会注意到 CocoaTouch 已经有一个基于 NSNotificationNSNotificationCenter 类的观察者模式。基本上,观察员注册是集中的。没有分散到观察到的类。
  • 这个库似乎有一些优点,至少在决定你自己的实现时值得一试:github.com/slazyk/Observable-Swift

标签: swift interface observer-pattern


【解决方案1】:

以上所有答案都错误地使用了数组来保留观察者,这可能会因为强引用而产生保留循环。

一般来说,您可能不希望同一个观察者注册自己两次。

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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 我相信private var observers: [Observing] 中存在一个错误,getter 将始终返回一个空数组:]
    【解决方案3】:

    协议是一组抽象的要求,在许多(可能非常不同的)其他对象之间共享。因此,将数据存储在协议中是不合逻辑的。这就像全球状态。我可以看到您想要定义观察者如何存储的规范。这也将允许“你”将“其他人”从观察者中移除,并且对观察者的存储方式非常严格。

    因此,您的协议应该公开添加和删除“您自己”作为观察者的方法。然后,实现协议的对象负责决定观察者的存储方式和位置,以及实现添加和删除。


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

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

    【讨论】:

    • 我完全同意你对协议的定义。但是,我使用该协议只是因为没有强制子类覆盖方法的选项。这才是我真正想要的……
    • 您应该得到未实现协议功能的构建错误
    • 是的,但我不能在协议中添加存储的属性,例如数组,这是观察者模式所需要的!
    • 看起来不错,要补充一点是Observation类应该实现Observer协议,以防Observer发生变化
    • @evya Observation 实现一个函数 notify 是为了方便演示用法,它可以是在实际观察者上调用 notify 的任何函数...
    【解决方案4】:

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

    虽然扩展不能存储属性,但您实际上可以使用 Objective-C 运行时获取它们。假设您有一个传感器基类 (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 和所有 Sensor 都从它继承才能拥有此属性,但 BaseSensor 实际上不会实现 Sensor 协议。 这有点奇怪,但我认为它会满足您的需求:

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

    使用 Swift 2.0 这会简单得多,因为您可以使用协议扩展,所以您可以简单地这样做:

    protocol Sensor {
      func switchOn()
    }
    
    extension Sensor {
      // Here the code from the previous implementation of the extension of BaseSensor
    }
    
    class LightSensor : Sensor {
       func switchOn() {
         // whatever
       }
    }
    

    好多了。

    【讨论】:

    • 感谢您的贡献,但回到 Objective-C 吗?苹果,认真的吗? :-)
    猜你喜欢
    • 2016-02-20
    • 2023-04-10
    • 1970-01-01
    • 1970-01-01
    • 2013-02-12
    • 2014-10-14
    • 1970-01-01
    • 2013-04-11
    • 2016-08-15
    相关资源
    最近更新 更多