协议导向编程和委托模式

9
一段WWDC 2015会议视频介绍了“面向协议编程”的概念,我想在我的未来应用程序中采用这种技术。为了理解这种新的方法,我最近几天一直在研究Swift 2.0,并试图将其与代理模式结合起来运用。
我有两个协议,定义了我项目中有趣部分的基本结构(示例代码是无意义的,但描述了问题):
1) 一个委托协议,类似于UITableViewController的dataSource协议,可以访问某些信息:
protocol ValueProvider {
    var value: Int { get }
}

2) 一个实体的接口协议,用于处理来自上面的信息(这就是所谓的“协议优先”方法的构想):

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 playground中看到:

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

另一个选项可能是向协议定义中添加类约束。但是,据我所知,这将破坏POP方法。

总之,我的问题可以归结为以下内容:如何在不限制协议设计过程中使面向协议编程和委托模式共同工作?

1个回答

3

事实证明在Playgrounds中使用autoreleasepool不适合证明引用循环。实际上,在将代码作为CommandLine应用程序运行时,代码中没有引用循环。问题仍然存在,是否这是最佳方法。它可以工作,但看起来有点巧妙。

此外,我对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  
}

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接