Reputation: 18729
So far I have only worked on Objectiv-C projects and now started my first Swift project.
I know that Swift does not support abstract classes but I would like to know what is the best way to model / solve this in 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
}
}
Of course it would be possible to define someProperty
as optional SomeBaseClass?
but in this case every time the property is used it has to be tested and unwrapped.
Is there a better way to solve this?
EDIT:
I know that Swift uses protocols to create an abstraction similar to abstract classes. However I do not understand how this concept can solve the concrete problem / question.
In other programming languages the abstract class MyClass
can use the property someProperty
in many different places while leaving the burden to initialize the property with a value with its concrete subclasses.
Although I read the article linked by @MohamendS and the answers to the possible dublicate answer I do not understand how to achieve the same using protocols.
MyClass
has only one abstract function while all other functions are implemented. Thus MyClass itself cannot be a protocol since protocols cannot implement functions (can they?)MyClass
could only implement another protocol which defines that there has to be a initProperties
method. But in this case MyClass
would need to provide an implementation of this method which brings us back to the same problem.I guess I can't see the wood for the trees, but how can protocols help here?
Upvotes: 3
Views: 1362
Reputation: 5088
Abstraction concept in Swift
is used with protocols
, i suggest reading this article to know more and here is an example
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() {
}
}
Edit: on your point, that
protocols can't implement functions
You can't but what you can do is extends those protocols using extension and give them an initial implementation therefore you don't have to implement them in the class and you can when you feel you'd like to, check the code below
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,
Now to use init
inside the extension
body so you wont have to type them in each confirmation, check out the code below
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")
}
}
Upvotes: 4
Reputation: 299345
There are many possible answers to this question depending on how MyClass is used. As written, there's of course no reason for someProperty
to be in the base class at all, and there's definitely no reason for an initProperties()
method (that should be in init
).
I realize this is "just an example," but it demonstrates a common problem of creating hierarchies that aren't needed. There are ways to write this code using a semi-abstract base class, but generally that should be avoided, so the first question is what you're using this for and can we avoid this problem entirely?
To answer the question as given, you'd probably start by making a default SomeBaseClass
, so that the abstract class can just assign someProperty = SomeBaseClass()
.
If that's impossible, generally you'd use a !
type:
let someProperty: SomeBaseClass!
And you implement initProperties()
with a fatalError
:
func initProperties() { fatalError("Implement in subclass") }
Alternately, it can be convenient to implement someProperty
as a computed variable, and implement it based on some other property in the subclasses
var someProperty: SomeBaseClass { fatalError() }
But this is really a last resort. Any time you find yourself having to write fatalError
you're probably on the wrong track, and you don't need a trick to get around it; you need to reconsider the problem.
You should first think about how MyClass
is used, and consider whether it can be a value type. Separately, you should think about whether it can be a protocol that matches the use case. Protocols are not just abstract interfaces that hide implementations. They are a view onto a conforming type to solve a specific problem. That's why there's a Collection protocol that provides access to dozens of algorithms for numerous, otherwise unrelated types, not an ArrayProtocol
just to hide the implementation of Array. Don't turn MyClass
into MyClassProtocol
. Ask what kinds of algorithms want to use types like this one.
When you find yourself creating interlocking hierarchies of types (subclasses of something that require subclasses of some other thing), you have often sliced the problem in the wrong direction. You should rethink whether you could slice the problem so that the varying parts of SomeBaseClass
are actually part of MyClass
(often this makes SomeBaseClass
simpler; for example being pure data rather than having logic).
There's no one right answer here. It depends on the nature of MyClass
, so we can't really discuss it in abstract terms. Like abstract classes, solving abstract problems often leads you down the wrong roads. It's often better to start with concrete types and then find their similarities and extract them.
Even with that said, it's worth showing what a simple, naive protocol would look like here. (It's possible this is even the correct protocol.)
public protocol MyProtocol {
var someProperty: SomeBaseClass { get }
}
That's it. That's all you actually express in your current abstract class (and it's not actually clear whether someProperty
is public; if it's private, this protocol would be empty).
An implementing struct would then look like:
struct MyStruct: MyProtocol {
var someProperty: SomeBaseClass
}
Or if you wanted a reference type, you could use a final class:
final class MyClass: MyProtocol {
var someProperty: SomeBaseClass
init() {
someProperty = ...
}
}
Or if you wanted inheritance, you could use a non-final class:
class MyClass: MyProtocol {
var someProperty: SomeBaseClass
init() {
someProperty = ...
}
}
Upvotes: 3