仅使用协议和协议扩展在 Swift 中生成自动递增的实例 ID

Generating auto-incrementing Instance IDs in Swift using Protocols and protocol-extensions only

目标

创建具有以下行为的“AutoIDable”协议。

  1. 每个符合此协议的 class 实例都将获得一个自动生成的字符串类型的“id”属性。
  2. 代码应生成 <prefix><Instance-count-starting-from-1> 格式的 ID 字符串(例如:E-1E-2、...E-<n> 等 1st 和 2nd。 .. 符合条件的第 n 个实例 class.
  3. 协议和协议扩展应该做 ALL 所需的工作来生成 id 字符串。符合要求的 class 只需订阅协议,仅此而已。

当前状态:

我通过以下实施实现了目标 1 和目标 2:

protocol Identifiable {
    var id: String { get }
}

protocol AutoIDable: Identifiable{
    static var _instanceCount: Int { get set }
}

class AutoID: AutoIDable {
    init(idPrefix: String) {
        setAutoID(prefix: idPrefix)
    }

    internal static var _instanceCount: Int = 0
    var id: String = ""
    
    func setAutoID(prefix: String = ""){
        Self._instanceCount += 1
        self.id = "\(prefix)\(Self._instanceCount)"
    }
}

class Employee: AutoID {
    init(){
        super.init(idPrefix: "E-")
    }
}

let e1 = Employee()
let e2 = Employee()
let e3 = Employee()
print(e1.id)
print(e2.id)
print(e3.id)
print(e1.id)

以上代码运行的输出:

E-1
E-2
E-3
E-1

待办事项:

为了实现目标 3,我需要消除 AutoID superclass 并使用协议扩展实现相同的功能。

我 运行 陷入麻烦是因为:

  1. 协议扩展不允许静态存储属性。我确实知道如何在不使用 superclass.
  2. 的情况下解决此限制
  3. 我不知道如何将代码注入到 Employee class 的创建者可能创建的所有初始化程序中。同样,如果不使用 superclass.
  4. 我想不出解决方法

如果你能指出正确的方向,我将不胜感激。

PS:Swift 编程新手。如果您有以更“快速”的方式实施代码的建议,请告诉我。 :-)

既然要使用协议,协议中不能有存储的属性。因此,如果不是 ID 本身,您将需要一些地方来存储递增的 ID 值。

不确定它是否违反了您仅使用协议的要求,因为它需要一种存储类型,但至少它不需要符合 classes 来拥有一个超级class .

所以,假设我们构建了这样一个 class 来保存所有 ID 并保持递增计数器:

class AutoIncrementId {
    static private var inc: Int = 0
    static private var ids: [ObjectIdentifier: String] = [:]

    static func getId(_ objectId: ObjectIdentifier, prefix: String) -> String {
        if let id = ids[objectId] { return id }
        else {
            inc += 1
            let id = "\(prefix)\(inc)"
            ids[objectId] = id
            return id
        }
    }
}

那么协议要求可以是:

protocol AutoIdentifiable {
    static var prefix: String { get }
    var id: String { get }
}

因此,class 需要定义其前缀。但是我们可以为 id:

定义默认实现
extension AutoIdentifiable where Self: AnyObject {
    var id: String {
        AutoIncrementId.getId(ObjectIdentifier(self), prefix: Self.prefix)
    }
}

用法为:

class Employee: AutoIdentifiable {
   static let prefix = "E-"
}

let e1 = Employee()
let e2 = Employee()
let e3 = Employee()
print(e1.id)  // E-1
print(e2.id)  // E-2
print(e3.id)  // E-3
print(e1.id)  // E-1