如何在Swift

时间:2019-03-19 12:50:44

标签: swift

到目前为止,我只从事Objectiv-C项目,现在开始了我的第一个Swift项目。

我知道Swift不支持抽象类,但是我想知道在Swift中建模/解决这个问题的最佳方法是什么:

// Abstract implementation
public abstract class MyClass {
    private SomeBaseClass someProperty; 

    MyClass() {
        initProperties();
    }

    abstract void initProperties();  // Init someProperty with some child class of SomeBaseClass.
}

// Objectiv-C
@implementation MyClass

- (id)init {
    self = [super init];

    if (self) {
        [self initProperties];     
    }

    return self;
}

- (void)initProperties {
    // Override in inherited classes
}


// Swift
class MyClass {
    // Should not be optional since MyClass should be required to have someProperty != nil
    let someProperty: SomeBaseClass;

    override init() {
        super.init();
        initProperties();
    }

    func initProperties() {
         // Cannot be empty since someProperty is non-optional and needs to be initialized
         // Cannot be empty since Swift does not support abstract methods
    }
}

当然可以将someProperty定义为可选的SomeBaseClass?,但是在这种情况下,每次使用该属性时,都必须对其进行测试和解包。

是否有更好的方法来解决这个问题?

编辑:

我知道Swift使用协议创建类似于抽象类的抽象。但是我不明白这个概念如何解决具体的问题。

在其他编程语言中,抽象类MyClass可以在许多不同的地方使用属性someProperty,同时留下了用其具体子类的值初始化属性的负担。

尽管我阅读了@MohamendS链接的文章以及可能的重复答案的答案,但我不理解如何使用协议来实现相同目的。

  • MyClass仅具有一个 abstract 函数,而其他所有函数均已实现。因此MyClass本身不能成为协议,因为协议不能实现功能(可以吗?)
  • MyClass只能实现另一个协议,该协议定义必须有一个initProperties方法。但是在这种情况下,MyClass需要提供该方法的实现,这使我们回到了相同的问题。

我想我看不见树木的树林,但是协议在这里有什么帮助?

2 个答案:

答案 0 :(得分:2)

Swift中使用了protocols中的抽象概念,我建议阅读this文章以了解更多信息,这是一个示例

 protocol Abstraction {
    var foo: String { get }
    func fee()
    init(with foo: String)
}

class A: Abstraction {

    required init(with foo: String) {
        self.foo = foo 
    }
    var foo: String = ""

    func fee() {

      }
}

编辑:就您而言,

  

协议无法实现功能

您不能但可以做的是使用扩展名扩展这些协议并为其提供初始实现,因此您不必在类中实现它们,并且可以在需要时进行检查。下面的代码

    class A: Abstraction {

    required init(with foo: String) {
        self.foo = foo
    }
    var foo: String = ""

    //you don't have to implement func foo anymore as its extended in the extension
}

extension Abstraction {
    func fee() {
        print("ok")
    }
}

let a = A(with: "foo")
a.fee() // this will trigger the extension implementation,

现在可以在init正文中使用extension了,因此您不必在每个确认中都键入它们,请查看下面的代码

protocol Abstraction {
    var foo: String { get set }
    func fee()
    init()
    init(with foo: String)
}


class A: Abstraction {
    required init() { }
    var foo: String = ""

    //you don't have to implement func foo anymore as its extended in the extension 
   // you don't have to implement the custom init anymore too 

}

extension Abstraction {
    init(with foo: String) {
        self.init()
        self.foo = foo
    }
    func fee() {
        print("ok")
    }
}

答案 1 :(得分:2)

根据使用MyClass的方式,有许多可能的答案。按照书面规定,someProperty当然根本没有理由在基类中,而且绝对没有理由使用initProperties()方法(应该在init中)。

我意识到这只是“一个例子”,但它展示了创建不需要的层次结构的常见问题。有一些方法可以使用半抽象基类来编写此代码,但通常应避免这种情况,因此第一个问题是您将其用于什么目的,我们是否可以完全避免此问题?

要回答给定的问题,您可能首先要设置一个默认的SomeBaseClass,以便抽象类仅可以分配someProperty = SomeBaseClass()

如果这不可能,通常使用!类型:

let someProperty: SomeBaseClass!

然后用initProperties()实现fatalError

func initProperties() { fatalError("Implement in subclass") }

或者,可以方便地将someProperty实现为计算变量,并基于子类中的其他一些属性实现

var someProperty: SomeBaseClass { fatalError() }

但这确实是不得已的方法。每当您发现自己不得不写fatalError时,您就可能走错了路,并且不需要技巧就可以解决它。您需要重新考虑问题。

您应该首先考虑MyClass的使用方式,并考虑是否可以将其用作值类型。另外,您应该考虑它是否可以是一个与用例匹配的协议。协议不仅仅是隐藏实现的抽象接口。它们是用于解决特定问题的合格类型的视图。这就是为什么有一个Collection协议可以为许多其他类型的不相关类型提供对数十种算法的访问,而不是ArrayProtocol只是为了隐藏Array的实现。不要将MyClass变成MyClassProtocol。问问什么样的算法要使用像这样的类型。

当您发现自己创建了类型互锁的层次结构(某些事物的子类需要其他事物的子类)时,通常会将问题切入错误的方向。您应该重新考虑是否可以对问题进行分解,以使SomeBaseClass的各个部分实际上是MyClass的一部分(通常这使SomeBaseClass更简单;例如,纯数据而不是逻辑)

这里没有正确的答案。它取决于MyClass的性质,因此我们不能真正抽象地讨论它。像抽象类一样,解决抽象问题通常会使您走错路。通常最好先从具体类型开始,然后找到它们的相似性并提取它们。

即使这样说,也值得在这里展示一个简单,幼稚的协议。 (这甚至可能是正确的协议。)

public protocol MyProtocol {
    var someProperty: SomeBaseClass { get }
}

就是这样。这就是您在当前抽象类中实际表达的全部内容(并且实际上someProperty是否公开;如果为私有,则该协议为空)。

然后,实现结构将如下所示:

struct MyStruct: MyProtocol {
    var someProperty: SomeBaseClass
}

或者,如果您想要引用类型,则可以使用最终类:

final class MyClass: MyProtocol {
    var someProperty: SomeBaseClass
    init() {
        someProperty = ...
    }
}

或者,如果您想要继承,则可以使用非最终类:

class MyClass: MyProtocol {
    var someProperty: SomeBaseClass
    init() {
        someProperty = ...
    }
}