这是归结的情况:
假设 Alice Allman 编写的第三方框架提供了一个非常有用的类:
public class AATrackpad {
public var cursorLocation: AAPoint = .zero
}
Bob Bell 编写的另一个框架提供了一个不同的类:
public class BBMouse {
public var where_is_the_mouse: BBPoint = .zero
}
在运行时,可能需要这些类中的任何一个,具体取决于用户决定使用的硬件。因此,根据依赖倒置原则,我不希望自己的类型依赖AATrackpad
或BBMouse
直接依赖。相反,我想定义一个协议来描述我需要的行为:
protocol CursorInput {
var cursorLocation: CGPoint { get }
}
然后让我自己的类型使用该协议:
class MyCursorDescriber {
var cursorInput: CursorInput?
func descriptionOfCursor () -> String {
return "Cursor Location: \(cursorInput?.cursorLocation.description ?? "nil")"
}
}
我希望能够使用一个实例BBMouse
作为光标输入,如下所示:
let myCursorDescriber = MyCursorDescriber()
myCursorDescriber.cursorInput = BBMouse()
但为了编译它,我必须追溯符合BBMouse
我的协议:
extension BBMouse: CursorInput {
var cursorLocation: CGPoint {
return CGPoint(x: self.where_is_the_mouse.x, y: self.where_is_the_mouse.y)
}
}
现在我已经符合BBMouse
我的CursorInput
协议,我的代码可以编译,我的架构就是我想要的方式。我在这里没有问题的原因是我认为where_is_the_mouse
该属性的名称很糟糕,我很高兴再也不会使用该名称。然而,有AATrackpad
一个不同的故事。我碰巧认为 AlicecursorLocation
完美地命名了她的属性,并且如您所见,我希望能够为我的协议要求使用相同的名称。我的问题是它AATrackpad
不用CGPoint
作此属性的类型,而是使用称为AAPoint
. 我的协议要求 ( cursorLocation
) 与 的现有属性具有相同名称AATrackpad
但类型不同的事实意味着我不能追溯符合CursorInput
:
extension AATrackpad: CursorInput {
var cursorLocation: CGPoint { // -- Invalid redeclaration
return CGPoint(x: self.cursorLocation.x, y: self.cursorLocation.y) // -- Infinite recursion
}
}
正如该片段中的注释所说,这段代码无法编译,即使编译了,我也会在运行时面临无限递归,因为我无法具体AATrackpad
引用cursorLocation
. 如果这样的事情可行,那就太好了(self as? AATrackpad)?.cursorLocation
,但我认为这在这种情况下没有意义。尽管如此,协议一致性甚至不会首先编译,因此为了解决无限递归而消除歧义是次要的。
考虑到所有这些背景,我的问题是:
如果我使用协议(被广泛推荐,这是有充分理由的)来构建我的应用程序,那么我使用某种第三方具体类型的能力真的取决于希望这个第三方开发人员不会分享我的口味吗?命名约定?
注意:“只需选择一个与您要使用的类型不冲突的名称”的答案不会令人满意。也许一开始我只有BBMouse
并且没有冲突,然后一年后我决定我也想添加支持AATrackpad
。我最初选择了一个很棒的名字,现在它在我的应用程序中普遍使用 - 为了一种新的具体类型,我是否必须在任何地方更改它?当我想添加对 的支持时会发生什么CCStylusTablet
,现在与我选择的任何新名称冲突?我是否必须再次更改协议要求的名称?我希望你明白我为什么要寻找比这更合理的答案。