面向协议编程和委托模式
Protocol Oriented Programming and the Delegate Pattern
一个WWDC 2015 session video describes the idea of Protocol-Oriented Programming, and I want to adopt this technique in my future apps. I've been playing around with Swift 2.0 for the last couple of days in order to understand this new approach, and am stuck at trying to make it work with the Delegate Pattern.
我有两个协议定义了我项目中有趣部分的基本结构(示例代码是废话但描述了问题):
1) 一个 委托协议 可以访问一些信息,类似于 UITableViewController 的 dataSource 协议:
protocol ValueProvider {
var value: Int { get }
}
2) 实体的 接口协议 对来自上面的信息执行某些操作(这里是 "Protocol-First" 方法的想法发挥作用的地方):
protocol DataProcessor {
var provider: ValueProvider { get }
func process() -> Int
}
关于数据处理器的实际实现,我现在可以在枚举、结构和 classes 之间进行选择。我想如何处理信息有几个不同的抽象级别,因此 classes 似乎最适合(但我不想将其作为最终决定,因为它可能会在未来的用例中发生变化)。我可以定义一个基本处理器 class,在其上我可以构建多个特定于案例的处理器(结构和枚举不可能):
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
}
}
到目前为止,一切都很顺利。但是,实际上,特定的数据处理器与所处理的值紧密相关(与基本处理器相反,not true),因此我想集成ValueProvider直接进入subclass(对比:通常,UITableViewControllers是他们自己的dataSource和delegate)。
首先我想到了添加一个带有默认实现的协议扩展:
extension DataProcessor where Self: ValueProvider {
var provider: ValueProvider { return self }
}
如果我没有不想绑定值的 BaseDataProcessor class,这可能会起作用。但是,继承自 BaseDataProcessor 和 的子classes 采用 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!
另一种选择可能是向协议定义添加 class 约束。但是,据我所知,这会破坏 POP 方法。
最后,我想我的问题可以归结为以下几点:如何在协议设计期间让面向协议的编程和委托模式一起工作而不限制自己 class 约束?
It turns out 在 Playgrounds 中使用 autoreleasepool 不适合证明引用循环。事实上,代码中没有引用循环,当代码是 运行 作为 CommandLine 应用程序时可以看出。问题仍然存在,这是否是最好的方法。它可以工作,但看起来有点老套。
此外,我对 BaseDataProcessors 和 SpecificDataProcessors 的初始化不太满意。 BaseDataProcessors 不应该知道子 类 w.r.t 的任何实现细节。 valueProvider 和 sub类 应该谨慎对待自己是 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
}
如果你有更好的想法,请告诉我:)
一个WWDC 2015 session video describes the idea of Protocol-Oriented Programming, and I want to adopt this technique in my future apps. I've been playing around with Swift 2.0 for the last couple of days in order to understand this new approach, and am stuck at trying to make it work with the Delegate Pattern.
我有两个协议定义了我项目中有趣部分的基本结构(示例代码是废话但描述了问题):
1) 一个 委托协议 可以访问一些信息,类似于 UITableViewController 的 dataSource 协议:
protocol ValueProvider {
var value: Int { get }
}
2) 实体的 接口协议 对来自上面的信息执行某些操作(这里是 "Protocol-First" 方法的想法发挥作用的地方):
protocol DataProcessor {
var provider: ValueProvider { get }
func process() -> Int
}
关于数据处理器的实际实现,我现在可以在枚举、结构和 classes 之间进行选择。我想如何处理信息有几个不同的抽象级别,因此 classes 似乎最适合(但我不想将其作为最终决定,因为它可能会在未来的用例中发生变化)。我可以定义一个基本处理器 class,在其上我可以构建多个特定于案例的处理器(结构和枚举不可能):
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
}
}
到目前为止,一切都很顺利。但是,实际上,特定的数据处理器与所处理的值紧密相关(与基本处理器相反,not true),因此我想集成ValueProvider直接进入subclass(对比:通常,UITableViewControllers是他们自己的dataSource和delegate)。
首先我想到了添加一个带有默认实现的协议扩展:
extension DataProcessor where Self: ValueProvider {
var provider: ValueProvider { return self }
}
如果我没有不想绑定值的 BaseDataProcessor class,这可能会起作用。但是,继承自 BaseDataProcessor 和 的子classes 采用 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!
另一种选择可能是向协议定义添加 class 约束。但是,据我所知,这会破坏 POP 方法。
最后,我想我的问题可以归结为以下几点:如何在协议设计期间让面向协议的编程和委托模式一起工作而不限制自己 class 约束?
It turns out 在 Playgrounds 中使用 autoreleasepool 不适合证明引用循环。事实上,代码中没有引用循环,当代码是 运行 作为 CommandLine 应用程序时可以看出。问题仍然存在,这是否是最好的方法。它可以工作,但看起来有点老套。
此外,我对 BaseDataProcessors 和 SpecificDataProcessors 的初始化不太满意。 BaseDataProcessors 不应该知道子 类 w.r.t 的任何实现细节。 valueProvider 和 sub类 应该谨慎对待自己是 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
}
如果你有更好的想法,请告诉我:)