协议之间的实现冲突

10

我遇到了一个问题,但是我无法想出解决方法。

假设我们有一个基类(可能来自FrameworkA),其中有一个名为subject的属性:

public class MyClass {
    public var subject: String
}

我们有一个协议(可能来自FrameworkB),具有另一个属性但名称相同:

而且,我们有一个协议(可能来自 FrameworkB),其具有另一个属性,但名称相同:

public protocol MyProtocol {
    var subject: String { get }
}

这两个属性代表完全不同的东西。

我该如何创建一个继承自 MyClass 并实现 MyProtocol 的类? 以及我应该如何使用这些属性?

public class SecondClass: MyClass, MyProtocol {
    var MyProcotol.name: String { // <==== Obviously not allowed
        return "something"
    }
    var MyClass.name: String { // <==== Obviously not allowed
        return "something else"
    }
}

我认为C#允许类似这样的声明,但我不是100%确定...


3
你需要根据实际情况来命名事物。如果它们不同,你就需要使用不同的名称。 - qwerty_so
当然可以 :) 但是,如果我无法改变它,例如基类和协议是某些框架的一部分(甚至是不同的框架),那该怎么办? - BPCorp
1
@ThomasKilian 我不同意你的观点,这是一个非常好的问题。解决方案并不总是将事物的名称设置正确,像这样的事情可能会发生,语言(编译器)必须有解决方案。 - Victor Sigler
好的,为了解决这个问题,你需要向苹果发送一个错误报告并等待几年。那样并没有什么帮助。真正有用的是,如果任何声明协议的人使用合理的名称,然后你相应地命名你的方法。 - gnasher729
是的,框架开发者应该使用明智的命名方式。但是应用程序开发者也应该停止将单个类尝试实现过多不同(在这种情况下相互冲突的)目的。 - Rob
@VictorSigler 我并没有说这是一个不好的问题。我只是在告诉你用Swift是不可能的。 - qwerty_so
2个回答

3
如果您对这个基类和/或协议有控制权,您可以给属性取唯一的名称来解决问题。
如果您不能这样做,问题就变成了SecondClass是否真的必须自己履行这两个相互矛盾的职责。具体来说,MyProtocol是什么?一些代理协议吗?您可以让SecondClass提供一个符合该协议的单独对象,消除冲突的双重角色。
总之,与其让一个单一对象尝试服务于两个相互冲突的目的,不如将责任分割成不同的对象以解决此类冲突。

1

好的,暂时让我们从使用这些类的代码的角度来看待这个问题,忽略它们是如何实现的。

事实1

如果secondClass:SecondClass扩展了MyClass,那么我希望能够编写:

secondClass.subject

事实2

如果secondClass符合MyProtocol,那么我期望能够编写以下代码

secondClass.subject

如果我们为secondClass创建一种不同的方式来暴露MyClass.subjectMyProtocol.subject,我们将打破面向对象的范例。

事实上

  1. 当你扩展一个类时,你隐式继承了所有非私有属性和方法,你不能(这是一件好事)重命名它们。
  2. 当你遵循一个协议时,你会暴露所有非可选声明的属性和方法,就像在协议中描述的那样。
如果SecondClass像你的伪代码中那样重命名了这两个属性,我们将无法编写像这样的东西。
let list : [MyClass] = [MyClass(), SecondClass()]

for elm in list {
    println(elm.subject)
}

一种(部分)可能的解决方案

你应该避免使用是一个方法,而应该采用拥有一个方法。

class SecondClass {
    let myClass : MyClass
    let somethingElse : MyProtocol

    init(myClass : MyClass, somethingElse:MyProtocol) {
        self.myClass = myClass
        self.somethingElse = somethingElse
    }

    var myClassSubject : String { get { return myClass.subject } }
    var myProtocolSubject : String { get { return somethingElse.subject } }
}

现在由于 SecondClass 既不是 MyClass,也不是 MyProtocol,因此它没有 subject 属性,从而消除了任何混淆的风险。
希望这有所帮助。

@Victor Sigler:感谢您进行更好的格式设置! - Luca Angeletti
我喜欢您的答案,但我认为苹果需要在未来的Swift版本中进行修正,因为编译器必须知道如何处理这种情况。 - Victor Sigler
2
这里的问题是我们正在处理与OOP相关的概念约束。我真的不知道语言本身如何改变以管理这种情况。 - Luca Angeletti
Traits的原始提案[Traits: Composable Units of Behaviour](http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf)描述了用于解决冲突的工具,包括方法的*别名*和*排除*(请参见3.5 Conflict Resolution)。这可能会对您感兴趣;) - kean

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接