jeudi 15 décembre 2016

Writing a common subject-observer implementation in Swift

Let's say I have a class that implements a beautiful subject-observer pattern thus. (This is Swift 3; Swift 2 would be no different in essence.)

protocol Delegate : class
{
    func method()
}

class Subject
{
    private typealias WeakDelegate = WeakReference< Delegate >
    private var nextAvailableDelegateId = 0
    private var delegates = [ Int : WeakDelegate ]()

    @discardableResult
    public func addDelegate( _ delegate: Delegate ) -> Int
    {
        let id = nextAvailableDelegateId
        nextAvailableDelegateId += 1
        delegates[ id ] = WeakDelegate( value: delegate )
        return id
    }

    public func removeDelegate( _ idForDelegate: Int )
    {
        delegates.removeValue( forKey: idForDelegate )
    }

    fileprivate func eachDelegate( fn: (Delegate) -> Void )
    {
        for (key, weakDelegate) in delegates
        {
            // Has this weak delegate reference become nil?
            //
            guard let delegate = weakDelegate.value else
            {
                // Yes. Remove it.
                delegates.removeValue( forKey: key )
                continue
            }

            fn( delegate )
        }
    }

    private func exampleNotifier()
    {
        eachDelegate{ $0.method() }
    }
}

(I'm taking the idiomatic Swift term "delegate" as roughly equivalent to the design pattern concept "observer".)

The WeakReference type above isn't strictly speaking part of this question, but in case you're curious:

public class WeakReference< T >
{
    public var value: T?
    {
        return abstractValue as? T
    }

    public init( value: T )
    {
        abstractValue = value as AnyObject
    }

    private weak var abstractValue: AnyObject?
}

Now I want to create another class analogous to Subject with another delegate protocol analogous to Delegate. How do I use the implementation I've already written for Subject in the new class?

One answer is to copy and paste the code. Not a good answer.

In C++ we could create a true mixin, a class that contains all the code and data necessary to implement the Subject, templated on a generic Delegate type, and inherit from it wherever we want to make some other class act as a Subject. Quite trivial.

Protocols, protocol extensions, and generics seem to have some of the machinery necessary for this kind of code reuse, but I can't work out how to accomplish it.

Help?

Aucun commentaire:

Enregistrer un commentaire