ios - 如何用 where 子句重构这个 Swift 协议(protocol)?

标签 ios swift protocols

我有一个 Savable具有多个版本 save() 的协议(protocol)基于C1的一致性的方法和 C2 .然后还有一个Downloadable符合 Savable 的协议(protocol)调用 Savable 的协议(protocol)的save()基于C1的一致性的方法和 C2 .
从下面的示例代码可以看出,download()方法都是一样的。有没有办法通过删除重复的功能来简化这段代码?还是我应该使用另一种完全不同的方法来获得相同的结果?
PS:两个SavableDownloadable由于某些未提及的技术原因,需要成为协议(protocol)。此外,C1C2两者都有 Self 或关联的类型要求,因此无法执行类型检查。

// Constraint
protocol C1 { }
protocol C2 { }

// Savable
protocol Savable { }
extension Savable {
    func save() {
        print("Perform basic save")
    }
}

extension Savable where Self: C1 {
    func save() {
        print("Perform save C1")
    }
}

extension Savable where Self: C1 & C2 {
    func save() {
        print("Perform save C1 & C2")
    }
}

// Downloadable
protocol Downloadable: Savable { }
extension Downloadable {
    func download() {
        print("Perform download")
        save()
    }
}

extension Downloadable where Self: C1 {
    func download() {
        print("Perform download")
        save()
    }
}

extension Downloadable where Self: C1 & C2 {
    func download() {
        print("Perform download")
        save()
    }
}

// Usage
class MyClassC1: Downloadable, C1 { }
let obj1 = MyClassC1()
obj1.download()
// Output:
// Perform download
// Perform save C1

class MyClassC1C2: Downloadable, C1, C2 { }
let obj2 = MyClassC1C2()
obj2.download()
// Output:
// Perform download
// Perform save C1 & C2
提前致谢!

最佳答案

这种方法非常脆弱。您依靠编译时检查来执行重载,并且只有在编译时知道类型时才有效。只有在 save() 的版本之间没有功能差异时才应该做这种事情。 (例如,如果唯一的区别是性能)。协议(protocol)不是子类。
作为问题的一个例子,考虑:

func getit(what: Downloadable) {
    what.download()
}

getit(what: obj2) // => "Perform basic save", not "perform save C1 & C2"
即使您添加泛型,它也会以完全相同的方式“失败”:
func getit<D: Downloadable>(what: D) { ... }
这并不是真正的失败。所有编译器在编译时都知道这是可下载的类型,因此它将调用可下载的实现。
如果你想要运行时调度,你需要在运行时使用 as? 进行调度。 .摆脱所有 where扩展和替换 save和:
protocol Savable { }
extension Savable {
    func save() {
        if self is C1 & C2 {
            print("Perform save C1 & C2")
        } else if self is C1 {
            print("Perform save C1")
        } else {
            print("Perform basic save")
        }
    }
}
注意顺序很重要(在测试 C1 & C2 之前测试 C1 )。决定如何调度取决于您。
不幸的是,这要求您在编译时静态地了解所有类型并将它们放在一个地方。可以解决这个问题并集中注册“保护程序”,但这有点棘手,除非你真的需要它,否则我不特别推荐这个。然而,这就是你将如何做到的。
// A Saver object that knows all the "rules". It maps conformances to functions
class Saver {
    static let shared = Saver()

    private var savers: [(predicate: (Any) -> Bool, save: (Any) -> ())] = []

    func addSaver<T>(of: T.Type, save: @escaping (T) -> ()) {
        savers.append((predicate: { $0 is T }, save: { save($0 as! T) }))
    }

    func save(value: Any) {
        for (predicate, save) in savers {
            if predicate(value) {
                save(value)
                return
            }
        }
        fatalError("Couldn't save") // Or some default behavior or whatever
    }
}

// Now you configure the Saver. Order is important!
let saver = Saver.shared
saver.addSaver(of: (C1 & C2).self, save: { _ in print("Perform save C1 & C2") })
saver.addSaver(of: C1.self, save: { _ in print("Perform save C1") })
saver.addSaver(of: Savable.self, save: { _ in print("Perform basic save") })

// Savable
protocol Savable { }
extension Savable {
    func save() {
        // And use it here
        Saver.shared.save(value: self)
    }
}
使用它,您可能会发现许多小角落案例。就像如果有人想在程序中的某个随机点(可能在另一个模块中?)添加另一个保护程序会发生什么,所以您可能需要重新排序测试,或者如果两个保护程序同样“特定”所以你不知道哪个一个更喜欢。这些正是编译器在处理此类问题时面临的极端情况。

关于ios - 如何用 where 子句重构这个 Swift 协议(protocol)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66585126/

相关文章:

swift - sqlite3结果集在swift中返回额外的数据

json - 如何在 SwiftUI 中解码来自 API 的 JSON 响应以在 View 中使用

frameworks - IoTivity 与 AllJoyn - 有什么区别?

cocoa - 抽象类或协议(protocol),Cocoa 推荐的做法是什么?

ios - 当短信到达 ios 7 时,是否还有其他可用的 API 或委托(delegate)方法

ios - 在 Swift 中向属性文本添加属性

ios - iPhone 照片库应用程序需要帮助,请

ios - 如何在 IOS 7 的表格 View 单元格中获取触摸文本框索引

macos - 在 Swift/Mac OS X 中编译着色器

protocols - 如何使用@Published 属性包装器定义协议(protocol)以包含属性