如何使用面向协议的编程来改进我的 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 类型的值。
我有一个以这种格式构建的相当大的项目:
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 类型的值。