I have this code in C# and want to model it in Swift. Since there is no abstract class/function in Swift and Swift is protocol oriented, this pattern becomes invalid. So I want to know best practice for this case.
abstract class Bird
{
string name
{
get;
set;
}
public abstract string family ();
public void print ()
{
Console.WriteLine ($ "Family: {family()} | Name: {name}");
}
}
class Eagle:Bird
{
public override string family ()
{
return "Accipitridae";
}
}
class Turaco:Bird
{
public override string family ()
{
return "Musophagidae";
}
}
I have done some research in StackOverflow, read medium posts and here are the solutions I have found. Now the question is which solution is in "swifty" way and doesn't seem odd?
- Just throw an error from abstract methods in base class:
class Bird {
...
func family() -> String {
fatalError("Not implemented")
}
}
- Move all abstract methods to a protocol, keep an instance of that protocol in Base class, conform child to the protocol and pass self to base as the instance of the protocol:
protocol BirdDelegate: AnyObject { // Or maybe not delegate? BirdProtocol???
func family() -> String
}
class Bird {
...
weak var delegate: BirdDelegate?
func print() {
guard let delegate = delegate else {
return
}
print("Family: \(delegate.family()) | Name: \(name)");
}
}
class Eagle: Bird, BirdDelegate {
override init() {
super.init()
delegate = self
}
func family() -> String {
return "Accipitridae";
}
}
- Move all abstract methods to a protocol, conform child to the protocol and try to cast the base to that protocol:
protocol BirdDelegate: AnyObject { // Or maybe not delegate? BirdProtocol???
func family() -> String
}
class Bird {
...
func print() {
guard let selfDelegate = self as? BirdDelegate else {
return
}
print("Family: \(delegate.family()) | Name: \(name)");
}
}
class Eagle: Bird, BirdDelegate {
func family() -> String {
return "Accipitridae";
}
}
- Make bird protocol:
protocol Bird {
var name: String {get set}
func family() -> String
func print() -> Void
}
extension Bird: {
func print() {
...
}
}
4.1. To avoid declaring all stored properties in each child class, a base class can be created:
class BirdBase {
var name: String = ""
}
class Eagle: BirdBase, Bird {
...
}
Some comments about solutions
It would be nice to have all errors at compile time.
-
- Children are not forced to implement the
family()
function and the error will appear at runtime. Besides that throwing an "unimplemented" error doesn't sound good to me.
- Children are not forced to implement the
-
- Children are forced to implement all functions of the protocol but since the protocol instance in Base class is optional, children can extend the Base class and not pass the delegate which will lead to a runtime error or a situation where
print()
function is useless (this solution is similar toUITableView
,UITableViewDatSource
, but I'm not sure it is perfect for this case). Same for (3.).
- Children are forced to implement all functions of the protocol but since the protocol instance in Base class is optional, children can extend the Base class and not pass the delegate which will lead to a runtime error or a situation where
-
- Everything is safe, because errors are known at compile time. The downside of this solution when you are working with classes is declaring all stored properties in each child class.
4.1. We have
BaseBird
class which doesn't havefamily()
property. Seems like it's incomplete and useless class.
CodePudding user response:
I think inheritance is better than protocols in this scenario.
class Bird {
let family: String
init(family: String) {
self.family = family
}
func printFamily() {
print("name: \(family)")
}
}
class Eagle: Bird {
override init(family: String) {
super.init(family: family)
}
override func printFamily() {
// super runs the print in the bird class
super.printFamily()
// Do whatever you want if you want to add something else
print("\(family) does also have beaks")
}
}
let eagleClass = Eagle(family: "Accipitridae")
eagleClass.printFamily()
CodePudding user response:
You can do something like this:
Declare a protocol Bird
and provide a default implementation of a print
method (which can be overridden if needed). Then you define a specific struct (Eagle
, Pigeon
, etc.) that provides a family as a computable string (or as a constant at the initialisation time).
protocol Bird {
var name: String { get set }
var family: String { get }
func print()
}
extension Bird {
func print() {
Swift.print("Family: \(family) | Name: \(name)")
}
}
struct Eagle: Bird {
var name: String
var family: String {
"Accipitridae"
}
}
let myPet = Eagle(name: "Buddy")
myPet.print()
You can also achieve something similar with a base class:
class BaseBird: Bird {
var name: String
let family: String
init(name: String, family: String) {
self.name = name
self.family = family
assert(type(of: self) != BaseBird.self, "Use a subclass of \(BaseBird.self)")
}
}
final class Eagle: BaseBird {
init(name: String) {
super.init(name: name, family: "Accipitridae")
}
}
let myPet = Eagle(name: "Buddy")
myPet.print()