2015-06-22 4 views
4

Я хочу реализовать шаблон наблюдателя, но я не нашел подходящих конструкторов языка программирования в Swift (также 2.0). Основные проблемы:Шаблон наблюдателя в Swift

  1. и extension не позволяет сохранять сохраненные свойства.
  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 
     } 
    } 
} 

Но это не очень удобно, потому что оба Sensor и SensorProtocol должны прийти а также оба требования, которые должен выполнить подкласс LightSensor.

Любые идеи?

+0

Это, вероятно, не представляется возможным, потому что вы не можете создать ВАР в расширение права? В моем случае это работает на обычном классе –

+0

Да, но, как я писал, в классе я не могу применять метод, который требуется реализовать! –

+0

Я обновил демо-код, чтобы уточнить, чего я хочу, а что невозможно. –

ответ

5

Протокол представляет собой абстрактный набор требований, разделяемых несколькими (потенциально очень разными) другими объектами. Таким образом, нелогично хранить данные в протоколе. Это было бы похоже на глобальное государство. Я вижу, что вы хотите определить спецификацию того, как хранятся наблюдатели. Это также позволит «вам» удалить «кого-то» из наблюдателя, и это очень ограничительно о том, как хранятся наблюдатели.

Итак, ваш протокол должен выставлять методы для добавления и удаления «себя» в качестве наблюдателя. Тогда ответственность за реализацию протокола заключается в том, чтобы решить, как и где хранятся наблюдатели, и реализовать добавление и удаление.


Вы можете создать на структуру для работы с протоколами, что-то вроде:

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

Я полностью согласен с вашим определением о протоколе. Тем не менее, я использую протокол только потому, что нет возможности заставить подкласс переопределить метод. Это то, что я действительно хочу ... –

+0

Вы должны получить ошибки сборки для неработающих функций протокола. – Wain

+0

Да, но я не могу добавить хранимые атрибуты в такие протоколы, как массив, что мне понадобится для шаблона наблюдателя! –

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 фактически не реализовал бы протокол 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 
    } 
} 

Путь лучше.

+0

Спасибо за ваш вклад, но вернемся к Objective-C? Apple, серьезно? :-) –

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

Я верю, что в 'private var наблюдателях есть ошибка: [Observing]', где получатель всегда будет возвращать пустой массив:] – pxpgraphics

0

Все ответы выше неправильно использовать массив для сохранения наблюдателей, которые могут создавать сохранить циклы из-за сильных ссылок.

Также вы можете не захотеть, чтобы один и тот же наблюдатель дважды регистрировался.

Представленные решения также не являются безопасными для общего назначения или отсутствия безопасности. Я ссылаться на моем блоге здесь, который представляет собой полное решение в порядке Swifty:

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

Смежные вопросы