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

aha*_*ese 9 delegates design-patterns protocols ios swift

一个WWDC 2015年会议的视频介绍的想法面向协议编程,我想通过我的未来应用这种技术.在过去的几天里,我一直在玩Swift 2.0,以便了解这种新方法,并坚持尝试使用Delegate Pattern.

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

1)一个委托协议,它使一些信息可访问,类似于UITableViewController的dataSource协议:

protocol ValueProvider {
    var value: Int { get }
}
Run Code Online (Sandbox Code Playgroud)

2)实体的接口协议,它使用上面的信息(这里是"协议优先"方法的想法发挥作用):

protocol DataProcessor {
    var provider: ValueProvider { get }
    func process() -> Int
}
Run Code Online (Sandbox Code Playgroud)

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

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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

extension DataProcessor where Self: ValueProvider {
    var provider: ValueProvider { return self }
}
Run Code Online (Sandbox Code Playgroud)

如果我没有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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

aha*_*ese 3

事实证明,在 Playgrounds 中使用autoreleasepool不适合证明引用循环。事实上,代码中没有引用循环,从代码作为命令行应用程序运行时可以看出。问题仍然是这是否是最好的方法。它可以工作,但看起来有点老套。

另外,我对BaseDataProcessorsSpecificDataProcessors的初始化不太满意。BaseDataProcessors不应该知道子类关于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  
}
Run Code Online (Sandbox Code Playgroud)

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