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

时间:2017-01-20 19:50:09

标签: ios swift protocol-oriented

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

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($0) })
    }
}

Numbers有许多方法,例如printMe(),我希望所有子类的任何实例都可以调用。

Numbers还有一个allIntegers()函数,我希望这些子类的任何实例都能够调用。作为一个变量,这可能会更好吗?但我无法覆盖子类中的变量。因此,我在每个子类中使用相同的私有变量integers,由allIntegers()读取并返回。

另请注意Numbers本身的实例永远不应该调用allIntegers(),只应在子类上调用它。

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

我已经阅读过面向协议的编程,感觉解决方案可能就在那里,或者我感谢任何其他建议和创造性方法来构建更好的项目。

谢谢!

修改

所有子类都不同,因为它们也有自己的功能。我已更新代码以反映这一点。

想象一下,像One这样的给定类在整个代码库中被多次初始化,并且始终使用完全相同的integers进行初始化。把:

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

整个代码库中的所有内容都容易出错。

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

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

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($0) })
    }
}

3 个答案:

答案 0 :(得分:4)

更新

根据您在问题中添加的新信息,这是一种可行的方法

我真的无法抗拒所以我稍微重构了命名:D

现在你有一个协议

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

和添加printMe函数的协议扩展。

extension HasIntegers {
    func printMe() {
        integers.forEach { print($0) }
    }
}

最后你有2个类(代码中有4个类但想法没有改变)。

A类总是包含[1, 2, 3, 101, 102]并拥有自己的方法集(doSomething()

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

班级B始终包含[​​1,2,3,201],并且有一套不同的方法(doSomethingElse()

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

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

  

老答案

您不需要

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

  • 继承
  • 协议(确实没有协议:D)
  • 计算属性
  • 功能
  • 课程(很多人!)

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

你可以简单地写

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

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

    func printMe() {
        integers.forEach { print($0) }
    }
}

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])

答案 1 :(得分:2)

使用常用操作定义协议,包括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({ "\($0)" }).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({ "\($0)" }).joined(separator: " "))
    }

}

然后我们可以实现符合协议的类。我们可以使用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)")
    }

}

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

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

对于协议中声明的方法,运行属于值的运行时类型的版本。对于未在协议中声明的方法,运行属于该值的编译时类型的版本。

答案 2 :(得分:1)

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

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

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($0) })
    }
}

然后让我们从objects()创建一个变量,正如您在问题中所说的那样(在这种情况下objects是只读的,您无法在实例中更新它):

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($0) })
    }
}

如果objects可以是只读的,您仍然可以为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($0) })
    }
}