【问题标题】:Implementation conflicts between protocols协议之间的实现冲突
【发布时间】:2015-04-30 11:57:08
【问题描述】:

我偶然发现了一个问题,我不知道该如何解决。

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

public class MyClass {
    public var subject: String
}

我们有一个协议(可能来自 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% 确定...

【问题讨论】:

  • 您需要相应地命名事物。如果它们不同,则需要不同的名称。
  • 是的,当然 :) 但是,如果我对此无能为力,例如,基类和协议是某些框架(甚至是单独的框架)的一部分,该怎么办?
  • @ThomasKilian 我不同意你的观点,这是一个很好的问题,解决方案并不总是相应地设置事物的名称,这样的事情可能会发生并且语言(编译器)必须是有解决办法
  • 好的,所以要解决问题,您可以向 Apple 发送错误报告并等待几年。那没有帮助。如果有人在声明协议时使用了合理的名称,那么您可以相应地为您的方法命名。
  • 是的,框架开发人员应该使用合理的名称。但应用程序开发人员也应该停止使用单个类来尝试实现太多不同(在这种情况下是相互冲突的)目的。

标签: swift oop inheritance protocols implementation


【解决方案1】:

如果您可以控制此基类和/或协议,则可以为属性指定唯一名称,从而解决问题。

如果你不能这样做,问题就变成了SecondClass 是否真的必须自己履行这两个相互冲突的责任。具体来说,MyProtocol 是什么?一些委托协议?您可以改为让SecondClass 提供一个符合该协议的单独对象,从而消除冲突的双重角色。

最重要的是,与其让单个对象尝试服务于两个相互冲突的目的,不如将职责拆分为单独的对象来解决任何此类冲突。

【讨论】:

    【解决方案2】:

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

    事实 1

    如果 secondClass : SecondClass extends 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)
    }
    

    可能的(部分)解决方案

    您应该避免使用 is-a 方法,而使用 has-a

    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 } }
    }
    

    现在

    1. SecondClass 不是 MyClass
    2. SecondClass 不是 MyProtocol

    它没有消除任何混淆风险的 subject 属性。

    希望这会有所帮助。

    【讨论】:

    • @Victor Sigler:感谢您提供更好的格式!
    • 我喜欢你的回答,但我认为 Apple 需要在未来的 Swift 版本中纠正这个问题,因为编译器必须知道如何处理这种情况。
    • 这里的问题是我们正在处理与 OOP 相关的概念约束。我真的不知道如何更改语言本身来管理这种情况。
    • 对特征Traits: Composable Units of Behaviour 的原始提案描述了用于解决此类冲突的工具,其中包括方法的别名排除(请参阅3.5 冲突解决)。可能对你感兴趣;)
    猜你喜欢
    • 1970-01-01
    • 2015-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多