如何使用面向协议的编程来改进我的 Swift 代码?

How to use protocol oriented programming to improve my Swift code?

我有一个以这种格式构建的相当大的项目:

class One : FirstThree {

    fileprivate var integers: [Int] {
        return [1, 2, 3, 101, 102]
    }

    override func allIntegers() -> [Int] {
        return integers
    }

    func doStuffForOne() {
        //does stuff unrelated to the other classes
    }
}

class Two : FirstThree {

    fileprivate var integers: [Int] {
        return [1, 2, 3, 201]
    }

    override func allIntegers() -> [Int] {
        return integers
    }

    func doStuffForTwo() {
        //does stuff unrelated to the other classes
    }
}

class Three : Numbers {

    fileprivate var integers: [Int] {
        return [301, 302, 303]
    }

    override func allIntegers() -> [Int] {
        return integers
    }

    func doStuffForThree() {
        //does stuff unrelated to the other classes
    }
}

class FirstThree : Numbers {

    fileprivate var integers: [Int] {
        return [1, 2, 3]
    }

    override func allIntegers() -> [Int] {
        return integers
    }

    func doStuffForFirstThree() {
        //does stuff unrelated to the other classes
    }
}

class Numbers {

    func allIntegers() -> [Int] {
        fatalError("subclass this")
    }

    func printMe() {
        allIntegers().forEach({ print([=11=]) })
    }
}

Numbers 有很多像 printMe() 这样的方法,我希望我所有子 class 的任何实例都能够调用。

Numbers 也有一个 allIntegers() 函数,我希望这些子 classes 的任何实例都能够调用。作为变量,哪个可能更好?但是我不能覆盖 subclass 中的变量。因此,我在每个子 class 中使用相同的私有变量 integers,它由 allIntegers().

读取和返回

同时注意 Numbers 的实例本身不应该调用 allIntegers(),它应该只在子 class.

上调用

最后,请注意一些子classes 包含相同的对象1, 2, 3,然后每个都有一些自定义整数。但不是所有的 subclasses。如果我后来决定所有这些子 class 都需要一个 4 整数,我必须手动遍历每个 class 并将 4 打入数组,这是显然容易出错。

我已经阅读了面向协议的编程,感觉其中可能有一个解决方案,或者我会感谢任何其他建议和创造性方法来构建更好的项目。

谢谢!

编辑

所有子class都是不同的,因为它们也有自己的功能要执行。我更新了代码以反映这一点。

想象一下,给定的 class 就像 One 在整个代码库中被初始化了很多次,并且总是使用完全相同的 integers 进行初始化。放:

let one = One(integers: [1, 2, 3, 101, 102])

整个代码库都容易出错。

希望这能解决我提出的人为示例的一些问题。

解决方案

谢谢大家的帮助。这是我想出的解决方案(请假设所有 classes 都有自己独特的方法)。

class One : FirstThree {

    override init() {
        super.init()
        self.integers = super.integers + [101, 102]
    }
}

class Two : FirstThree {

    override init() {
        super.init()
        self.integers = super.integers + [201]
    }
}

class Three : Numbers {
    var integers  = [301, 302, 303]
}

class FirstThree : Numbers {
    let integers = [1, 2, 3]
}

protocol Numbers {
    var integers: [Int] { get }
    func printMe()
}

extension Numbers {
    func printMe() {
        integers.forEach({ print([=13=]) })
    }
}

我假设您有一个非常复杂的应用程序,这些带有一些虚拟函数的简单 classes 只是一个简单的例子。所以这是一种使用协议重构它的方法:

第一步可以是将 Numbers 基础 class 更改为具有默认实现的协议,例如:

class One : Numbers {

    fileprivate var _objects: [Int] {
        return [1, 2, 3, 101, 102]
    }

    func objects() -> [Int] {
        return _objects
    }
}

class Two : Numbers {

    fileprivate var _objects: [Int] {
        return [1, 2, 3, 201]
    }

    func objects() -> [Int] {
        return _objects
    }
}

class Three : Numbers {

    fileprivate var _objects: [Int] {
        return [1, 2, 3, 301, 302, 303]
    }

    func objects() -> [Int] {
        return _objects
    }
}

class Four : Numbers {

    fileprivate var _objects: [Int] {
        return [401, 402]
    }

    func objects() -> [Int] {
        return _objects
    }
}

protocol Numbers {
    func objects() -> [Int];
    func printMe() ;
}

//Default implementation of the services of Numbers.
extension Numbers {
    func objects() -> [Int] {
        fatalError("subclass this")
    }

    func printMe() {
        objects().forEach({ print([=10=]) })
    }
}

然后让我们从 objects() 创建一个变量,正如您在问题中所说的(在这种情况下 objects 是 read-only,您不能在实例中更新它):

class One : Numbers {

    //Protocol specifying only the getter, so it can be both `let` and `var` depending on whether you want to mutate it later or not :
    let objects = [1, 2, 3, 101, 102]

}

class Two : Numbers {

    var objects =  [1, 2, 3, 201]

}

class Three : Numbers {

    var objects  = [1, 2, 3, 301, 302, 303]

}

class Four : Numbers {

    let objects = [401, 402]

}

protocol Numbers {

    var objects:[Int] { get }
    func printMe() ;
}

//Default implementation of the services of Numbers.
extension Numbers {

    func printMe() {
        objects.forEach({ print([=11=]) })
    }
}

如果 objects 可以是 read-only,您仍然可以为 objects 添加默认实现 getter,例如:

class Four : Numbers {

}

protocol Numbers {

    var objects:[Int] { get }
    func printMe() ;
}

//Default implementation of the services of Numbers.
extension Numbers {

    var objects: [Int] {
        return [401, 402]
    }

    func printMe() {
        objects.forEach({ print([=12=]) })
    }
}

更新

鉴于您添加到问题中的新信息,这里有一个可能的方法

实在忍不住所以稍微重构了一下命名:D

现在你有了协议

protocol HasIntegers {
    var integers: [Int] { get }
    func printMe()
}

和添加 printMe 功能的协议扩展。

extension HasIntegers {
    func printMe() {
        integers.forEach { print([=11=]) }
    }
}

最后你有 2 个 classes(在你的代码中是 4 classes 但想法没有改变)。

class A 总是包含 [1, 2, 3, 101, 102] 并且有自己的一套方法 (doSomething())

class A: HasIntegers {
    fileprivate (set) var integers: [Int] = [1, 2, 3, 101, 102]
    func doSomething() { }
}

class B 总是包含 [1, 2, 3, 201] 并且有一组不同的方法 (doSomethingElse())

class B: HasIntegers {
    fileprivate (set) var integers: [Int] = [1, 2, 3, 201]
    func doSomethingElse() { }
}

A和B都符合HasInteger,然后自动接收printMe()方法


OLD ANSWER

你不需要那个

我在你的代码中看到了很多东西:

  • 继承
  • protocols(确实没有协议 :D)
  • 计算属性
  • 函数
  • class有(很多!)

但是没有明显的理由使用所有这些东西:)

你可以简单地写

class Box {
    fileprivate (set) var integers: [Int]

    init(integers:[Int]) {
        self.integers = integers
    }

    func printMe() {
        integers.forEach { print([=14=]) }
    }
}

let one = Box(integers: [1, 2, 3, 101, 102])
let two = Box(integers: [1, 2, 3, 201])
let three = Box(integers: [1, 2, 3, 301, 302, 303])
let four = Box(integers: [401, 402])

使用您的常用操作定义一个协议,包括 objects 访问器:

protocol Numbers {

    /// My objects. By default, `Numbers.commonObjects`. Subclasses can override to include more objects.
    var objects: [Int] { get }

    func printMeThatConformersCanOverride()

}

在扩展中提供默认实现:

extension Numbers {

    /// The default implementation of `objects`, which just returns `Numbers_defaultObjects`.
    var objects: [Int] { return Numbers_defaultObjects }

    /// Since this is declared in the protocol, conformers can override it.
    func printMeThatConformersCanOverride() {
        Swift.print("Numbers " + objects.map({ "\([=11=])" }).joined(separator: " "))
    }

}

/// It would be nice to make this a member of `Numbers`, but Swift won't let us.
private let Numbers_defaultObjects = [1, 2, 3]

因为这些定义实现了协议中声明的东西,符合类型可以覆盖它们。您还可以在扩展中定义符合类型无法覆盖的内容:

extension Numbers {

    /// Since this is not declared in the protocol, conformers cannot override it. If you have a value of type `Numbers` and you call this method on it, you get this version.
    func printMeThatConformersCannotOverride() {
        Swift.print("Numbers " + objects.map({ "\([=12=])" }).joined(separator: " "))
    }

}

然后我们可以实现一个符合协议的class。我们可以使用 let 来覆盖 objects:

class One: Numbers {

    /// You can use a `let` to override `objects`.
    let objects: [Int] = Numbers_defaultObjects + [101, 102]

    func doStuffForOne() {
        Swift.print("I'm doing One-specific stuff with \(objects)")
    }

    func printMeThatConformersCanOverride() {
        Swift.print("One wins! You don't care what type I am.")
    }

    func printMeThatConformersCannotOverride() {
        Swift.print("One wins! You think I'm type One, not type Numbers.")
    }

}

我们可以使用存储的 属性 来覆盖 objects:

class Two: Numbers {

    /// You can use a stored property to override `objects`.
    var objects: [Int] = Numbers_defaultObjects + [201]

    func doStuffForTwo() {
        Swift.print("I'm doing Two-specific stuff with \(objects)")
    }

}

我们可以使用计算的 属性 来覆盖 objects:

class Three: Numbers {

    /// You can use a computed property to override `objects`.
    var objects: [Int] { return [301, 302, Int(arc4random())] }

    func doStuffForThree() {
        Swift.print("I'm doing Three-specific stuff with \(objects)")
    }

}

我们甚至不必使用 class 类型。我们可以改用结构类型:

struct Four: Numbers {
    func doStuffForFour() {
        Swift.print("I'm doing Four-specific stuff with \(objects)")
    }
}

我在上面说过,您可以在扩展中定义事物,如果它们未在协议中声明,则符合类型不能覆盖它们。这在实践中可能有点混乱。如果您像 One 那样尝试覆盖在扩展中定义但不属于协议一部分的方法,会发生什么情况?

let one = One()

one.printMeThatConformersCanOverride()
// output: One wins! You don't care what type I am.

(one as Numbers).printMeThatConformersCanOverride()
// output: One wins! You don't care what type I am.

one.printMeThatConformersCannotOverride()
// output: One wins! You think I'm type One, not type Numbers.

(one as Numbers).printMeThatConformersCannotOverride()
// output: Numbers 1 2 3 101 102

对于协议声明的方法,你运行版本属于run-time类型的值。对于未在协议中声明的方法,您 运行 版本属于 compile-time 类型的值。