如何在Swift中实现像抽象属性这样的计算属性?

时间:2017-02-18 13:11:52

标签: ios swift

我知道Swift中没有抽象类和Abstract关键字。以下问题就像实现抽象属性一样。

为方便起见,假设有以下三个类:

class SuperClass: NSManagedObject { // the compiler will complain without 'NSManagedObject', and I don't know why.
    public let superInt: Int = 1 // use superInt to represent other stored property for SuperClass.
}

class SubClass1: SuperClass {
    let subInt1: Int = 2 // use 'subInt1' to represent other stored property for SubClass1.
}
class SubClass2: SuperClass {
    let subInt2: Int = 3 // use 'subInt2' to represent other stored property for SubClass2.
}
protocol TestProtocol {
    var count: Int { get } // a computed property
    func printInt() // a custom method
}

这里,这些类都是CoreData中定义的对象,尤其是SuperClass是一个抽象实体。我想为SuperClass扩展一些接口(上面的TestProtocol),以便我可以使用多态。我想出了两种方法:

方法1:让SuperClass确认TestProtocol

extension SuperClass: TestProtocol {
    var count: Int { return superInt }
    func printInt() { print("Here is SuperClass. Count: \(count)") }
}
extension SubClass1 {
    override var count: Int { return subInt1 }
    override func printInt() { print("Here is SubClass1. Count is \(count)") }
}
extension SubClass2 {
    override var count: Int { return subInt2 }
    override func printInt() { print("Here is SubClass2. Count is \(count)") }
}

// Here is the test code
let subClasses: [SuperClass] = [SubClass1(), SubClass2()]
subClasses.forEach { $0.printInt() }

方法2:将子类转换为协议对象。

extension SubClass1: TestProtocol {
    var count: Int { return subInt1 }
    func printInt() { print("Here is SubClass1. Count is \(count)") }
}
extension SubClass2: TestProtocol {
    var count: Int { return subInt2 }
    func printInt() { print("Here is SubClass1. Count is \(count)") }
}

// Here is the test code
let subClasses: [SuperClass] = [SubClass1(), SubClass2()]
subClasses.forEach { ($0 as! TestProtocol).printInt() }

在方法1中,一切看起来都不错。但我必须在SuperClass中实现完全没用的代码。这个方法看起来像是一个小小的语法技巧。

在方法2中,所有代码都很有用,但最后一行的转换破坏了代码的优雅。这让我疯狂地继续使用像($0 as! TestProtocol).printInt()这样的代码。

我对这两种方法都不满意。那么这是推荐的方式还是有更好的方法呢?

2 个答案:

答案 0 :(得分:0)

在你的第二种方法中,你实际上不需要使用强制转换(($0 as! TestProtocol).printInt())。方法如下:

let subClasses: [TestProtocol] = [SubClass1(), SubClass2()]
subClasses.forEach { $0.printInt() }

通过键入subClasses数组到TestProtocol而不是SuperClass,您可以消除对转换的需要,因为编译器现在知道subClasses中的每个元素都符合{{ 1}}因此,有一个方法TestProtocol

答案 1 :(得分:0)

您的超类在方法1和方法2中都没有任何用途。在您定义的两种方法中,您希望最终调用printInt(),其中应该是所有子类的共同。请注意我已突出显示"应该是常见的"。协议的目的是指定符合类型必须实现的要求。因此,协议足以形成您的类1和2应该实现var count和func printInt()的契约。协议也是一种类型,因此您不需要超类来保存对其他两个类的引用,如下所示:

    let subClasses: [SuperClass] = [SubClass1(), SubClass2()]

在我看来,这是一个更好的方法:

    protocol TestProtocol {
        var count: Int { get } // a computed property
        func printInt() // a custom method
    }

    Class1: TestProtocol {
        var subInt = 2
        var count: Int { return subInt1 }
        func printInt() { print("Here is SubClass1. Count is \(count)") }
    }

    Class2: TestProtocol {
        var subInt = 3
        var count: Int { return subInt2 }
        func printInt() { print("Here is SubClass2. Count is \(count)") }
    }

   let smallClasses: [TestProtocol] = [Class1(), Class2()]
   smallClasses.forEach { $0.printInt() }

这种做法在Swift中实现了面向协议的编程方式,这是一种受欢迎的方法。一般来说,在iOS设计模式中避免继承和子类化,我将引导您访问此博客文章,以便您可以了解有关https://krakendev.io/blog/subclassing-can-suck-and-heres-why

的更多信息。

修改

你已经澄清了你的问题,所以我会尝试更好地回答它。您正在寻找Java中抽象类的等价物,它是包含一个或多个抽象方法的类。 Java中的抽象方法是声明的方法,但不包含任何实现。抽象类可能无法实例化,并且需要子类来提供抽象方法的实现。

Swift没有相同的功能,但是功能相当的东西需要一个超类,其中所有子类都被迫实现必须对所有子类共同的属性或方法。这当然是通过我上面显示的协议方法实现的,但是你还需要能够从子类调用超类中的属性或方法,这意味着每个子类必须能够同时转换为协议类型和超类班级类型。

以下是我用Swift 3编写的解决方案:

    protocol TestProtocol {
      var count: Int { get } // a computed property
      func printInt() // a custom method
    }

    //base class

    class SuperClass: TestProtocol {

      var sharedInt: Int = 0

      var subInt: Int = 1
      var count: Int { return subInt }
      func printInt() { print("Here is SubClass. Count is \(count)") }

    }

    class class1: SuperClass {

      override init(){
      super.init()
      self.subInt = 2
    }

    }

    class class2: SuperClass {

      override init(){
      super.init()
      self.subInt = 3
   }

   }

   //I can get count and printInt() which superClass, class1 and class2 share becuase of the protocol.

    let smallClasses: [TestProtocol] = [SuperClass(), class1(), class2()]

    smallClasses.forEach { $0.printInt() }

    //I can convert the sub classes to super class type and call their printInt method

    let smallClasses2: [SuperClass] = [class1(), class2()]

    smallClasses2.forEach { $0.printInt() }

    //I can get to the shared values the sub classes have from the super class

    smallClasses2.forEach { print($0.sharedInt) }

如果您将上面的代码复制并粘贴到Xcode的Playground中,您将收到以下输出:

    Here is SubClass. Count is 1
    Here is SubClass. Count is 2
    Here is SubClass. Count is 3
    Here is SubClass. Count is 2
    Here is SubClass. Count is 3
    0
    0