[Swift] Protocol superiority over inheritance

I think the most common way to express an abstraction is ** class inheritance **.

So when expressing an abstract concept, it is not a structure I thought it would be better to define the type using a class.

However, ** Swift recommends using structs instead of classes. ** **

Which one should I use! !! At that time I remembered another way to represent the abstract concept of ** protocol **.

Structures and enums can embody abstract concepts in the form of protocol compliance.

Then use the protocol! So I would like to compare the two expression methods and give them superiority or inferiority.

Unexpected behavior of class inheritance

In the following sample code The Animals class, which represents the abstract concept of animals, We define Dog, WildHawk subclasses that represent the concrete things of dogs and wild hawks.

The behaviors common to all animals, "sleep" and "movement", and their owners are defined as superclasses.

Because each animal moves differently, Subclasses override the "move" method.


//Animal class(Super class)
class Animals {
    var owner: String?
    func sleep() {
        print("sleeping")
    }
    func move() {}
}

//Dog class
class Dog: Animals {
    override func move() {
        print("Running")
    }
}

//Wild hawk class
class WildHawk: Animals {
    override func move() {
        print("Flying")
    }
}

By using class inheritance, we were able to obtain the following behavior.

** ・ The variety of move () methods is realized. -The sleep () method can be used without implementing it in each subclass **

However, it also leads to the following unexpected behavior.

** ・ Because the Animals class is an abstract concept that does not represent a specific animal Instantiation should not be possible, but it can. ・ Wild Hawk is a wild hawk, so there should be no owner, The owner property is automatically added by inheritance. ** **

Unnecessary initializers and properties This is not desirable as it can lead to class misuse.

Overcoming the problem of protocol inheritance of classes

With protocols and protocol extensions ** You can overcome the problems of class inheritance while satisfying the behavior that can be achieved by class inheritance. ** **

Subclasses can only inherit one superclass, compared to The type can comply with multiple protocols.

So, if you want to represent the Animals class as a protocol, ** Can be divided into units of more appropriate meaning. ** **

In the following sample code The three elements defined in the Animals class represent the behavior of animals. ** Animal Protocol **, which includes two methods, sleep () and move (), and Divided into ** Ownable Protocol ** that contains an owner property that indicates that it can be kept.


protocol Ownable {
    var owner: String { get set }
}

protocol Animal {
    func sleep()
    func move()
}

extension Animal {
    func sleep() {
        print("Sleeping")
    }
}

struct Dog: Animal, Ownable {
    var owner: String
    func move() {
        print("Running")
    }
}

struct WildHawk: Animal {
    func move() {
        print("Flying")
    }
}

Compared to class inheritance The behavior that was realized by class inheritance is also realized by the protocol.

-The polymorphism of the move () method has been realized. **-> A common interface is implemented by the protocol **

-The sleep () method can be used without implementing it in each subclass. **-> Defines the default implementation of the sleep () method by extending the Animal protocol **

In addition, the unexpected behavior of class inheritance is overcome as follows:

・ Because the Animals class is an abstract concept that does not represent a specific animal Instantiation should not be possible, but it can. **-> Animal is a protocol and cannot be instantiated **

・ Wild Hawk is a wild hawk, so there should be no owner, The owner property is automatically added by inheritance. **-> Classes cannot be inherited multiple times, but types that comply with multiple protocols can be defined. Therefore, only the required types need to comply with the Ownable protocol. ** **

When to take advantage of class inheritance

However, it doesn't mean that you don't have to use class inheritance at all. Of course, there are cases where it is better to use class inheritance.

Sharing a stored property implementation between multiple types

With protocol extensions I've shown that you can share a default implementation between multiple types without using class inheritance.

** But with protocol extensions There are some cases where the implementation cannot be shared due to the limitation that the stored property cannot be implemented. ** **

In the following sample code A property observer is defined in the owner property of the Animal class. Since it is the didSet keyword, it will be executed at the initialized timing.


class Animal {
    var owner: String? {
        didSet {
            guard let owner = owner else {
                return
            }
            print("\(owner) was assigned as the owner")
        }
    }
}

let dog = Animal()
dog.owner = "Tanaka Tarou"

Execution result
Tanaka Tarou was assigned as the owner

If you try to implement this behavior with the protocol as before, it will be as follows.


protocol Ownable {
    var owner: String { get set }
}

extension Ownable {
    //Compile error
    var owner: String {
        didSet {
            print("\(owner) was assigned as the owner")
        }
    }
}

Error details: Extensions must not contain stored properties Japanese translation: Extensions cannot contain saved properties

If you want to implement the same behavior, the code will be as follows.


protocol Ownable {
    var owner: String { get set }
}

struct Dog: Ownable {
    var owner: String {
        didSet {
            print("\(owner) was assigned as the owner")
        }
    }
}

var dog = Dog(owner: "Tanaka Jirou")
dog.owner = "Tanaka Jirou"

Execution result
Tanaka Jirou was assigned as the owner

This is redundant code because the exact same part will appear in multiple places.

Also, when changing the behavior, it is necessary to correct all parts. It also makes the code vulnerable to change.

** So sharing an implementation that includes stored properties is It's better to use class inheritance instead of protocol. ** **

These are the advantages of the protocol over inheritance.

Basically, design with structure & protocol, Design with a combination of classes and inheritance only when sharing a stored property implementation.

Thank you for watching until the end.

Recommended Posts

[Swift] Protocol superiority over inheritance
[Swift] About protocol extension
[Swift] Protocol concept and definition method