简体   繁体   中英

How can I design a generic observer pattern protocol in Swift?

I want to design a generic Swift protocol for the observer pattern for use with different types/classes. The problem is that I can't seem to specify the type for the observer's notify() method.

Initially, I tried creating an associatedtype with my Observer protocol.

protocol Observer {
    associatedtype T
    func notify(_ value: T)
}

protocol Observable {
    var observers: [Observer] { get set }
    func registerObserver(_ observer: Observer)
    func unregisterObserver(_ observer: Observer)
}

This doesn't work (compilation error): error: protocol 'Observer' can only be used as a generic constraint because it has Self or associated type requirements .

So I tried using generic methods instead:

protocol Observer {
    func notify<T>(_ value: T)
}

protocol Observable {
    associatedtype T
    var observers: [Observer] { get set } // This is okay now
}

extension Observable {
    // implement registerObserver()
    // implement unregisterObserver()

    func notifyObservers<T>(_ value: T) {
        for observer in observers {
            observer.notify(value)
        }
    }
}

This works just fine, but leads to some pretty interesting result. To test it out, I created FooObserver and FooObservable :

class FooObserver: Observer {
    func notify<T>(_ value: T) {
        print(value)
    }
}

class FooObservable: Observable {
    typealias T = Int // For simplicity I set T to Int type
    var observers: [Observer] = []
}

let a = FooObserver()
let b = FooObserver()
var c = FooObservable()
c.registerObserver(a)
c.registerObserver(b)
c.notifyObservers("hello") // This works, but why?

I was able to successfully notify my 2 observers of the string "hello". I'm guessing this has to do with type erasure...?

So my question is: how can I implement a generic-type observer pattern where I can be sure that the value in notify() is of the correct type?

protocol Observer {
    associatedtype T
    func notify(_ value: T)
}

This says that a given observer can handle one specific type ( T ) that the observer chooses.

protocol Observable {
    var observers: [Observer] { get set }
    func registerObserver(_ observer: Observer)
    func unregisterObserver(_ observer: Observer)
}

This is invalid, because the system has no way to know what T you want for these observers. Each observer can only handle exactly one T .

protocol Observer {
    func notify<T>(_ value: T)
}

This is basically meaningless. It says that notify can be called with any type at all. If that's what you mean, you want to say:

protocol Observer {
    func notify(_ value: Any)
}

But that means that every observer has to deal with Any , which is not good. The reason it happens to work is that you chose print as your test. print can handle Any . When you want to test these kinds of things, you need to try something like what you really want to do in your program. Printing works for all kinds of things that are useless for any other purpose.

The basic problem is that Observer shouldn't be a protocol. It should be a function. For example:

typealias Observer<T> = (T) -> Void

protocol Observable {
    associatedtype T
    var observers: [Observer<T>] { get set }
    func registerObserver(_ observer: Observer<T>)
    func unregisterObserver(_ observer: Observer<T>)
}

The problem with this approach is there's no way to implement unregisterObserver . I'm not clear how you've implemented unregisterObserver in your code, either. It doesn't really look possible.

Here's one very simple way to build an observable:

typealias Observer<T> = (T) -> ()

struct Subscription {
    let cancel: () -> Void
}

final class Observable<T> {
    private var observations: [UUID: Observer<T>] = [:]
    func subscribe(observer: @escaping Observer<T>) -> Subscription {
        let uuid = UUID()
        observations[uuid] = observer
        return Subscription(cancel: { [weak self] in self?.observations[uuid] = nil })
    }
}

(See https://stackoverflow.com/a/55389143/97337 for where this comes from.)

For a more fully worked out version that is very simple, see Observable . For a somewhat more complicated version that is a little quirky, see Stream . For a more powerful and less quirky version, see Properties . For a very powerful version that establishes a whole way of programming, see RxSwift .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM