3

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?

OldPeculier
  • 11,049
  • 13
  • 50
  • 76
  • Maybe I misunderstand but can you not inherit from `Subject` for your new class? – sdasdadas Jan 24 '17 at 11:50
  • @sdasdadas Imagine that I need a new pair of classes, `NewSubject` and `NewDelegate`. The question is: how do I inherit `Subject` such that `NewDelegate` becomes the new delegate type, such that, for example, `eachDelegate()` takes a function taking a `NewDelegate`? – OldPeculier Feb 03 '17 at 10:42

1 Answers1

0

You can use protocol inheritance and generics to derive from some basic protocol.

Each new delegate will inherit from a parent class:

protocol Delegate: class {

    func method()
}

protocol DelegateA: Delegate { }

protocol DelegateB: Delegate { }

Your parent subject class can be implemented using a generic conforming to your parent protocol.

class Subject<T: Delegate> {

    private typealias WeakDelegate = WeakReference<T>
    private var nextAvailableDelegateId = 0
    private var delegates = [Int: WeakDelegate]()

    @discardableResult
    public func addDelegate(_ delegate: T) -> 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: (T) -> 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() }
    }
}

Each new subject can be instantiated as a generic conforming to your child delegates.

class SubjectA<T: DelegateA>: Subject<T> { }

class SubjectB<T: DelegateB>: Subject<T> { }
sdasdadas
  • 23,917
  • 20
  • 63
  • 148
  • 1
    Well, I wish that worked. But you can't use a protocol to instantiate a generic in Swift. Consequently, you can't inherit a `SubjectA` such that it can operate on a new protocol type of `DelegateA`. You would have to use a concrete type (some implementation of protocol `DelegateA`) to instantiate the SubjectA class, and then you've defeated the purpose of having a subject class with an observer protocol. See http://stackoverflow.com/q/33503602/358475. – OldPeculier Feb 03 '17 at 12:11
  • @OldPeculier I'm afraid I still don't understand. Something will have to be a concrete type when you do instantiate. I think maybe it's a bit too abstract for me to fully understand. Could you provide an example with `NewSubject` and `NewDelegate` implementations? I'll leave my answer up for a short while in case anyone can correct it. – sdasdadas Feb 03 '17 at 14:53
  • 1
    @OldPeculier Ah, I understand now that I've had a bit more time with it. I thought extensions would get around it but it seems that the issues lies solely with the fact that you can't use a protocol to conform to the generic argument. Wish I could've helped more! – sdasdadas Feb 03 '17 at 15:42