面向协议的编程和委托模式

时间:2015-09-06 15:06:14

标签: ios swift design-patterns delegates protocols

WWDC 2015 session video描述了面向协议的编程的想法,我想在未来的应用中采用这种技术。在过去的几天里,我一直在玩Swift 2.0,以便了解这种新方法,并坚持尝试使其与Delegate Pattern一起使用。

我有两个协议来定义项目中有趣部分的基本结构(示例代码是无意义的,但描述了问题):

1)一个委托协议,可以访问一些信息,类似于UITableViewController&#39> dataSource 协议:

protocol ValueProvider {
    var value: Int { get }
}

2)实体的接口协议,它使用上面的信息(这里是" Protocol-First"方法的概念)播放):

protocol DataProcessor {
    var provider: ValueProvider { get }
    func process() -> Int
}

关于数据处理器的实际实现,我现在可以在枚举,结构和类之间进行选择。我希望如何处理信息有几种不同的抽象级别,因此类似乎最适合(但我不想让这个作为最终决定,因为它可能在将来的用例中发生变化)。我可以定义一个基本处理器类,在此基础上我可以构建几个特定于案例的处理器(结构和枚举不可能):

class BaseDataProcessor: DataProcessor {
    let provider: ValueProvider

    init(provider: ValueProvider) {
        self.provider = provider
    }

    func process() -> Int {
        return provider.value + 100
    }
}

class SpecificDataProcessor: BaseDataProcessor {
    override func process() -> Int {
        return super.process() + 200
    }
}

到此为止,一切都像魅力一样。但是,实际上特定的数据处理器与处理的值紧密绑定(与基本处理器相反, <真>),这样我想集成 ValueProvider 直接进入子类(用于比较:通常,UITableViewControllers是他们自己的dataSource和delegate)。

首先,我想到添加一个带有默认实现的协议扩展:

extension DataProcessor where Self: ValueProvider {
    var provider: ValueProvider { return self }
}

如果我没有我不想创造价值约束的BaseDataProcessor类,这可能会有用。但是,继承自BaseDataProcessor 的子类采用ValueProvider似乎在内部覆盖该实现,因此这不是一个选项。

我继续尝试并最终得到了这个:

class BaseDataProcessor: DataProcessor {
    // Yes, that's ugly, but I need this 'var' construct so I can override it later
    private var _provider: ValueProvider!
    var provider: ValueProvider { return _provider }

    func process() -> Int {
        return provider.value + 10
    }
}

class SpecificDataProcessor: BaseDataProcessor, ValueProvider {
    let value = 1234

    override var provider: ValueProvider { return self }

    override func process() -> Int {
        return super.process() + 100
    }
}

哪些编译,乍一看似乎做我想要的。然而,这不是一个解决方案,因为它产生一个参考周期,可以在Swift游乐场中看到:

weak var p: SpecificDataProcessor!
autoreleasepool {
    p = SpecificDataProcessor()
    p.process()
}
p // <-- not nil, hence reference cycle!

另一种选择可能是将类约束添加到协议定义中。但是,正如我所理解的那样,这会打破POP方法。

总结,我认为我的问题归结为以下几点:如何在协议设计过程中将协议编程和委托模式协同工作而不限制类约束?

1 个答案:

答案 0 :(得分:3)

在Playgrounds中使用 autoreleasepool

It turns out不适合证明参考周期。实际上,代码中没有引用循环,这可以在代码作为CommandLine应用程序运行时看到。问题仍然是这是否是最佳方法。它有效,但看起来有点hacky。

另外,我对 BaseDataProcessors SpecificDataProcessors 的初始化不太满意。 BaseDataProcessors 不应该知道子类w.r.t的任何实现细节。 valueProvider ,子类应该谨慎自己是 valueProvider

目前,我已解决了初始化问题,如下所示:

class BaseDataProcessor: DataProcessor {  
    private var provider_: ValueProvider! // Not great but necessary for the 'var' construct  
    var provider: ValueProvider { return provider_ }  

    init(provider: ValueProvider!) {  
        provider_ = provider  
    }  

    func process() -> Int {  
        return provider.value + 10  
    }  
}  

class SpecificDataProcessor: BaseDataProcessor, ValueProvider {  
    override var provider: ValueProvider { return self } // provider_ is not needed any longer  

    // Hide the init method that takes a ValueProvider  
    private init(_: ValueProvider!) {  
        super.init(provider: nil)  
    }  

    // Provide a clean init method  
    init() {  
        super.init(provider: nil)  
        // I cannot set provider_ = self, because provider_ is strong. Can't make it weak either  
        // because in BaseDataProcessor it's not clear whether it is of reference or value type  
    }  

    let value = 1234  
}

如果您有更好的想法,请告诉我们:)