- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
这是归结的情况:
假设 Alice Allman 编写的第三方框架提供了一个非常有用的类:
public class AATrackpad {
public var cursorLocation: AAPoint = .zero
}
Bob Bell 编写的另一个框架提供了一个不同的类:
public class BBMouse {
public var where_is_the_mouse: BBPoint = .zero
}
在运行时,可能需要这些类中的任何一个,具体取决于用户决定使用的硬件。因此,与 Dependency Inversion Principle 保持一致,我不希望我自己的类型直接依赖于 AATrackpad
或 BBMouse
。相反,我想定义一个描述我需要的行为的协议(protocol):
protocol CursorInput {
var cursorLocation: CGPoint { get }
}
然后让我自己的类型改用该协议(protocol):
class MyCursorDescriber {
var cursorInput: CursorInput?
func descriptionOfCursor () -> String {
return "Cursor Location: \(cursorInput?.cursorLocation.description ?? "nil")"
}
}
我希望能够使用 BBMouse
的实例作为光标输入,如下所示:
let myCursorDescriber = MyCursorDescriber()
myCursorDescriber.cursorInput = BBMouse()
但为了编译它,我必须追溯地使 BBMouse
符合我的协议(protocol):
extension BBMouse: CursorInput {
var cursorLocation: CGPoint {
return CGPoint(x: self.where_is_the_mouse.x, y: self.where_is_the_mouse.y)
}
}
现在我已经使 BBMouse
符合我的 CursorInput
协议(protocol),我的代码可以编译并且我的架构是我想要的方式。我在这里没有问题的原因是我认为 where_is_the_mouse
是该属性的糟糕名称,我很高兴再也不会使用该名称。然而,对于 AATrackpad
来说,情况就不同了。我碰巧认为 Alice 完美地命名了她的 cursorLocation
属性,正如您所看到的,我希望能够为我的协议(protocol)要求使用相同的名称。我的问题是 AATrackpad
不使用 CGPoint
作为此属性的类型,而是使用称为 AAPoint
的专有点类型。事实上,我的协议(protocol)要求 (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
就好了,但我认为这在这种情况下没有意义。同样,协议(protocol)一致性甚至不会首先编译,因此为了解决无限递归而消除歧义是次要的。
考虑到所有这些背景,我的问题是:
如果我使用协议(protocol)构建我的应用程序(这是有充分理由广泛推荐的),那么我使用某种第三方具体类型的能力是否真的取决于该第三方开发人员不共享的希望我喜欢命名约定?
注意:“只需选择一个与您要使用的类型不冲突的名称” 的答案不会令人满意。也许一开始我只有 BBMouse
并且没有冲突,然后一年后我决定我也想添加对 AATrackpad
的支持。我最初选择了一个好名字,现在它在我的应用程序中普遍使用——我是否必须为了一种新的具体类型而在所有地方都更改它?稍后当我想添加对 CCStylusTablet
的支持时会发生什么,它现在与我选择的任何新名称冲突?我是否必须再次更改协议(protocol)要求的名称?我希望您明白我为什么要寻找比这更合理的答案。
最佳答案
受 Jonas Maier 评论的启发,我发现了我认为在架构上足以解决此问题的解决方案。正如乔纳斯所说,函数重载展示了我正在寻找的行为。我开始认为也许协议(protocol)要求应该只是函数,而不是属性。按照这种思路,我的协议(protocol)现在将是:
protocol CursorInput {
func getCursorLocation () -> CGPoint
func setCursorLocation (_ newValue: CGPoint)
}
(请注意,在这个答案中,我也将其设置为可设置,这与原始帖子不同。)
我现在可以追溯地使 AATrackpad
符合此协议(protocol)而不会发生冲突:
extension AATrackpad: CursorInput {
func getCursorLocation () -> CGPoint {
return CGPoint(x: self.cursorLocation.x, y: self.cursorLocation.y)
}
func setCursorLocation (_ newValue: CGPoint) {
self.cursorLocation = AAPoint(newValue)
}
}
重要 - 即使 AATrackpad
已经有一个同名函数 func getCursorLocation () -> AAPoint
,这仍然会编译但不同的类型。这种行为正是我在原始帖子中想要从我的属性(property)中得到的。因此:
在协议(protocol)中包含属性的主要问题是,由于命名空间冲突,它可能使某些具体类型实际上无法遵守该协议(protocol)。
以这种方式解决这个问题之后,我有一个新问题要解决:我希望 cursorLocation
成为一个属性而不是一个函数是有原因的。我绝对不想被迫在整个应用程序中使用 getPropertyName()
语法。值得庆幸的是,这可以解决,如下所示:
extension CursorInput {
var cursorLocation: CGPoint {
get { return self.getCursorLocation() }
set { self.setCursorLocation(newValue) }
}
}
这就是协议(protocol)扩展的妙处。协议(protocol)扩展中声明的任何内容都类似于函数的默认参数——只有在没有其他优先级时才使用。由于这种不同的行为模式,当我使 AATrackpad
符合 CursorInput
时,此属性不会引起冲突。我现在可以使用我最初想要的属性语义,而且我不必担心命名空间冲突。我很满意。
“等一下 - 现在 AATrackpad
符合 CursorInput
,不是吗有两个版本的 cursorLocation
?如果我要使用 trackpad.cursorLocation
,它会是一个 CGPoint
或一个 AAPoint
?
它的工作方式是这样的 - 如果在此范围内已知对象是 AATrackpad
,则使用 Alice 的原始属性:
let trackpad = AATrackpad()
type(of: trackpad.cursorLocation) // This is AAPoint
但是,如果已知类型仅为 CursorInput
,则使用我定义的默认属性:
let cursorInput: CursorInput = AATrackpad()
type(of: cursorInput.cursorLocation) // This is CGPoint
这意味着,如果我碰巧知道类型是 AATrackpad
,那么我可以像这样访问属性的任一版本:
let trackpad = AATrackpad()
type(of: trackpad.cursorLocation) // This is AAPoint
type(of: (trackpad as CursorInput).cursorLocation) // This is CGPoint
这也意味着我的用例完全解决了,因为我特别希望不知道我的cursorInput
是否恰好是一个AATrackpad
或 BBMouse
- 只是它是某种 CursorInput
。因此,无论我在哪里使用我的 cursorInput: CursorInput?
,它的属性都是我在协议(protocol)扩展中定义的类型,而不是类中定义的原始类型。
有一种可能性是,仅具有作为要求的功能的协议(protocol)可能会导致 namespace 冲突 - Jonas 在他的评论中指出了这一点。如果协议(protocol)要求之一是一个没有参数的函数,并且符合类型已经有一个具有该名称的属性,那么该类型将无法符合协议(protocol)。这就是为什么我一定要命名我的函数,包括动词,而不仅仅是名词 (func getCursorLocation () -> CGPoint
) - 如果任何第三方类型在属性名称中使用动词,那么我可能无论如何都不想使用它:)
关于Swift - 使第三方类型符合我自己的协议(protocol)但有冲突的要求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50963447/
我正在尝试编写一个相当多态的库。我遇到了一种更容易表现出来却很难说出来的情况。它看起来有点像这样: {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE
谁能解释一下这个表达式是如何工作的? type = type || 'any'; 这是否意味着如果类型未定义则使用“任意”? 最佳答案 如果 type 为“falsy”(即 false,或 undef
我有一个界面,在IAnimal.fs中, namespace Kingdom type IAnimal = abstract member Eat : Food -> unit 以及另一个成功
这个问题在这里已经有了答案: 关闭 10 年前。 Possible Duplicate: What is the difference between (type)value and type(va
在 C# 中,default(Nullable) 之间有区别吗? (或 default(long?) )和 default(long) ? Long只是一个例子,它可以是任何其他struct类型。 最
假设我有一个案例类: case class Foo(num: Int, str: String, bool: Boolean) 现在我还有一个简单的包装器: sealed trait Wrapper[
这个问题在这里已经有了答案: Create C# delegate type with ref parameter at runtime (1 个回答) 关闭 2 年前。 为了即时创建委托(dele
我正在尝试获取图像的 dct。一开始我遇到了错误 The function/feature is not implemented (Odd-size DCT's are not implemented
我正在尝试使用 AFNetworking 的 AFPropertyListRequestOperation,但是当我尝试下载它时,出现错误 预期的内容类型{( “应用程序/x-plist” )}, 得
我在下面收到错误。我知道这段代码的意思,但我不知道界面应该是什么样子: Element implicitly has an 'any' type because index expression is
我尝试将 SignalType 从 ReactiveCocoa 扩展为自定义 ErrorType,代码如下所示 enum MyError: ErrorType { // .. cases }
我无法在任何其他问题中找到答案。假设我有一个抽象父类(super class) Abstract0,它有两个子类 Concrete1 和 Concrete1。我希望能够在 Abstract0 中定义类
我想知道为什么这个索引没有用在 RANGE 类型中,而是用在 INDEX 中: 索引: CREATE INDEX myindex ON orders(order_date); 查询: EXPLAIN
我正在使用 RxJava,现在我尝试通过提供 lambda 来订阅可观察对象: observableProvider.stringForKey(CURRENT_DELETED_ID) .sub
我已经尝试了几乎所有解决问题的方法,其中包括。为 提供类型使用app.use(express.static('public'))还有更多,但我似乎无法为此找到解决方案。 index.js : imp
以下哪个 CSS 选择器更快? input[type="submit"] { /* styles */ } 或 [type="submit"] { /* styles */ } 只是好
我不知道这个设置有什么问题,我在 IDEA 中获得了所有注释(@Controller、@Repository、@Service),它在行号左侧显示 bean,然后转到该 bean。 这是错误: 14-
我听从了建议 registering java function as a callback in C function并且可以使用“简单”类型(例如整数和字符串)进行回调,例如: jstring j
有一些 java 类,加载到 Oracle 数据库(版本 11g)和 pl/sql 函数包装器: create or replace function getDataFromJava( in_uLis
我已经从 David Walsh 的 css 动画回调中获取代码并将其修改为 TypeScript。但是,我收到一个错误,我不知道为什么: interface IBrowserPrefix { [
我是一名优秀的程序员,十分优秀!