【问题标题】:Writing a common subject-observer implementation in Swift用 Swift 编写一个通用的主题观察者实现
【发布时间】:2016-12-15 17:55:09
【问题描述】:

假设我有一个类实现了一个漂亮的主题观察者模式。 (这是 Swift 3;Swift 2 本质上没有什么不同。)

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

(我将惯用的 Swift 术语“委托”大致等同于设计模式概念“观察者”。)

上面的WeakReference 类型严格来说不是这个问题的一部分,但如果你好奇的话:

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?
}

现在我想用另一个类似于Delegate 的委托协议创建另一个类似于Subject 的类。如何在新类中使用我已经为Subject 编写的实现?

一个答案是复制并粘贴代码。不是一个好的答案。

在 C++ 中,我们可以创建一个真正的 mixin,一个包含实现 Subject 所需的所有代码和数据的类,模板化为泛型 Delegate 类型,并在我们想让其他类充当 Subject 的任何地方从它继承.相当琐碎。

协议、协议扩展和泛型似乎具有这种代码重用所需的一些机制,但我不知道如何实现它。

帮助?

【问题讨论】:

  • 也许我理解错了,但是你不能为你的新班级继承 Subject 吗?
  • @sdasdadas 假设我需要一对新的类,NewSubjectNewDelegate。问题是:我如何继承Subject 以使NewDelegate 成为新的委托类型,例如eachDelegate() 采用采用NewDelegate 的函数?

标签: swift generics design-patterns


【解决方案1】:

您可以使用协议继承和泛型从一些基本协议派生。

每个新委托都将继承自父类:

protocol Delegate: class {

    func method()
}

protocol DelegateA: Delegate { }

protocol DelegateB: Delegate { }

您的父主题类可以使用符合您父协议的泛型来实现。

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

每个新主题都可以实例化为符合您的子代表的泛型。

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

class SubjectB<T: DelegateB>: Subject<T> { }

【讨论】:

  • 好吧,我希望这能奏效。但是你不能使用协议在 Swift 中实例化泛型。因此,您不能继承SubjectA,以便它可以在DelegateA 的新协议类型上运行。您必须使用具体类型(协议DelegateA 的某些实现)来实例化SubjectA 类,然后您就破坏了使用观察者协议的主题类的目的。见stackoverflow.com/q/33503602/358475
  • @OldPeculier 恐怕我还是不明白。当您实例化时,某些东西必须是具体的类型。我想也许这对我来说太抽象了,无法完全理解。你能提供一个NewSubjectNewDelegate 实现的例子吗?我会暂时保留我的答案,以防有人纠正。
  • @OldPeculier 啊,我现在明白了,我已经有更多的时间了。我认为扩展会解决它,但似乎问题仅在于您不能使用协议来符合通用参数这一事实。希望我能提供更多帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-03
  • 2019-08-22
  • 1970-01-01
  • 1970-01-01
  • 2020-02-10
  • 1970-01-01
相关资源
最近更新 更多