如何使用Swift协议扩展来创建可以修改私有属性的共享代码?

时间:2017-11-30 00:05:18

标签: swift inheritance protocols

假设情景:

有课程:ClassA;和ClassB。两者都实现ProtocolC,其中包含单个要求func createKey()ProtocolC有一个extension来实现createKey(),因为就此功能而言,ClassAClassB所需的功能是相同的。但是,createKey()的实现需要访问uniqueKeyClassA都包含的名为ClassB的私有变量,其值必须在两个类中不同(你可能想象的那样)。 extensioncreateKey()的{​​{1}}因此变得无用 - 因为变量是ProtocolC - 意味着其代码必须在fileprivate和{{1}中重复}}。但重复的代码是禁止编程的。解决方案可以使用超类而不是协议扩展,但这会削弱Swift试图实现的目标。继承是斯威夫特禁忌。

ClassA

ClassB需要锁定在两个类中,不能设置,也不能从没有。 Swift编写的解决方案是什么:可以访问私有属性的通用代码;同时避免继承?

感谢您的阅读。

2 个答案:

答案 0 :(得分:2)

这里的东西非常接近你的理想。缺点是扩展方法中运行的运行时协议,但它实现了您正在寻找的访问控制级别(fileprivate存储var)和通过协议扩展重用代码:

fileprivate protocol UniqueKeyDefining {
    var uniqueKey: String? { get set }
}

class ClassA: ProtocolC, UniqueKeyDefining {
    fileprivate var uniqueKey: String?
}

class ClassB: ProtocolC, UniqueKeyDefining {
    fileprivate var uniqueKey: String?
}

protocol ProtocolC {
    func createKey()
}

extension ProtocolC {
    func createKey(){
        if var hasUniqueKey = self as? UniqueKeyDefining {
            hasUniqueKey.uniqueKey = NSUUID().uuidString
        } else {
            // Some default behavior if the conformer to ProtocolC isn't also UniqueKeyDefining
        }
    }
}

协议UniqueKeyDefining在使用ClassAClassB声明的文件之外是不可见的,因此文件外的任何代码都不知道这些类符合它或者可以看到他们的uniqueKey ivars。

ProtocolC的特定协议扩展名在同一个文件中声明,因此 知道UniqueKeyDefining并且可以有条件地将self强制转换为该协议运行时通过该fileprivate协议访问uniqueKey ivar。

对于此文件之外的类,它似乎不具有可伸缩性或可重用性,但它符合我从您的问题中解释的一系列狭窄的要求。

答案 1 :(得分:1)

您正在尝试访问协议中未定义的内容。扩展应该仅基于其公共细节。你不应该访问他们的私人。

我认为你真正想要的是"分享实施"而不是与访问控制和类型系统斗争。如果您需要共享实现,只需进行显式实现并共享它们。

protocol ProtocolC {
    func createKey()
}

class ClassA: ProtocolC {
    private var impl = Impl()
    func createKey() {
        impl.createKey()
    }
}

class ClassB: ProtocolC {
    private var impl = Impl()
    func createKey() {
        impl.createKey()
    }
}

private struct Impl {
    private(set) var uniqueKey: String? 
    mutating func createKey(){ 
        // Lock here.
        uniqueKey = NSUUID().uuidString
        // Unlock here.
    }
}

避免对私有细节的依赖是extension的意图和设计目标之一,因为这种依赖会使代码变得脆弱,而这种脆弱性是子类化的主要问题之一。