到目前为止,我只从事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
需要提供该方法的实现,这使我们回到了相同的问题。我想我看不见树木的树林,但是协议在这里有什么帮助?
答案 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 = ...
}
}