Swift protocol composition

Composition, breaker of chains



As we have learned on our last blog post that, Inheritance is the starting point of code coupling. We also had a hint about breaking that code coupling through composition. Here on this blog post we will initiate the talk about composition on Swift. We will talk about how we can achieve composition on Swift through protocol. We will also talk about how the protocol is the breaker of the coupling chain.

The Swift Protocol Hub contains all the related posts with protocol. Be sure to check that out.

Background

Composition

What does composition means? It means constructing the whole from the components.
Rather than Inheriting from a single Base class, composition will construct the whole behavior by containing instances of component’s behavior.

On Inheritance all the common properties and functions are put on the Base class and then the derived classes get those properties and functions by Inheriting the Base class. As a result modifying any of the common properties and functions, on the Base class, become tough because the changes will have significant impact on all the derived classes.

On Swift Inheritance through Base class, is a class only property. No value type, ie enum struct, can have an inheritance through a base class. Value type can only Inherit from protocol.

On Composition the properties and functions are kept on different types, rather than on a single type as we did on Inheritance through Base class.
Then finally the target type possesses the required type’s instance among the different types. And the corresponding properties and functions are now available to the target type through that instance.

The target type is similar to derived class of Inheritance, but this time the derived type is having instances of common properties and functions rather than a strict Inheritance from the Base class. More importantly the target type can discard all the functionality which it may not need, now it can choose the functionality that it intends to have. Overall the code coupling seems to be removed through composition.

Protocol, Swift way of Composition

On Swift the composition is achieved through protocol. We can defined the requirements, computed property and methods, on a protocol. And can provide a default implementation if that make sense. Finally the target type can adopt the protocol. The target type can implements its own implementation of the computed property and methods of the protocol.

Example will clarify the issue.

Example

We will continue the example from our last blog post. We were building a Justice league. You can have a look at the code on GitHub which was done through Inheritance. But this time we will complete it through Composition.

We were stuck when we want to add TheFlash, as TheFlash does not have any cape. So a capeColor does not make any scene.

Here on this example we will still use the class, but from the next blog post for protocol series we will move to Value type, ie struct and enum.

So from the previous blog post example we will remove the capeColor from SuperHero and placed it under a Cape protocol.

protocol Cape{
    var capeColor: String { get }
}

class SuperHero{
    let name: String
    let originalName: String
    
    init(name: String, originalName: String) {
        self.name = name
        self.originalName = originalName
    }
    
    func speciality() -> String{
        return ""
    }
    
    var profile: String?{
        return "Known as: \(name) \n Original Name: \(originalName).\n Speciality is: \n\(speciality())"
    }
}

Also note we are currently changing the profile for not including the capeColor. On our next talk we will add it, as the topic is more suitable for the next blog post.

Now the first two founding member, Batman and WonderWoman, of Justice League will implement the capeColor by adopting the Cape protocol. As they have cape on their costume.

We will be still using the Inheritance from the base class, SuperHero, on this blog post.

Here comes the Batman 🦇.

class Batman: SuperHero{
    init() {
        super.init(name: "Batman", originalName: "Bruce Wayne")
    }
    
    override func speciality() -> String {
        return "Very techie and extremely cool 😎"
    }
}

extension Batman: Cape{
    var capeColor: String { return "Black" }
}

We implement the Cape protocol on the extension of Batman, why? Because it actually encapsulate the responsibilities.

Let print the profile and capeColor of Batman.

let batman = Batman()
batman.profile
batman.capeColor

Likewise WonderWoman:

class WonderWoman: SuperHero{
    init() {
        super.init(name: "Wonder Woman", originalName: "Diana")
    }
    
    override func speciality() -> String {
        return "Come on, she is a Demigoddess"
    }
}

extension WonderWoman: Cape{
    var capeColor: String { return "Red" }
}

let wonderWoman = WonderWoman()
wonderWoman.profile
wonderWoman.capeColor

Now finally we can introduce TheFlash to our Justice League.

class TheFlash: SuperHero{
    init() {
        super.init(name: "The Flash", originalName: "Barry Allen")
    }
    
    override func speciality() -> String {
        return "Runs so so fast"
    }
}

TheFlash().profile

TheFlash does not have a cape on his costume. So we will not adopt the Cape protocol.

Explanation of the Example

Now it is time to explain the example.

  • How code coupling is achieved?
  • How we are achieving the composition using protocol?

As the Justice League starts to grow, we immediately find out not all SuperHero will have a cape so capeColor is unnecessary as a common property on SuperHero. But the capeColor was tightly coupled with SuperHero on our first implementation for the first blog post, as it was defined inside of SuperHero. We have to move it out of SuperHero to break down the tight coupling.

After we move the capeColor on the Cape protocol, things seems to be more flexible. Because SuperHero with cape like Batman and WonderWoman can still have the capeColor by adopting the Cape protocol. On the other hand TheFlash can ignore that protocol as he does not have a cape. So it is a win win situation for all.

We broke the tight coupling through composition, more specifically through introducing protocol on our code base.

Q/A What we have to write the get inside of the var capeColor?

capeColor is a computed property and is defined on a protocol. So the compiler is forcing us write at-lest the getter, get, for that property. By writing down the getter, get, on the protocol now the implementation class have to provide the getter.
So basically we are setting constraints for the protocol adopted class when we write get inside the computed property definition.
If we wrote a setter, set, on the same way we did on the protocol definition, the the adopted class would be bound to provide the setter, set.

Multiple Inheritance

On Swift multiple Inheritance is possible only through protocol. That means no class can inherit multiple Base class.

Both the value type and reference type can have multiple inheritance by adopting multiple protocol. Let us add another protocol adaptation or inheritance on our existing example.

We will add another var power through protocol, which will provide the corresponding Super power that the superhero possesses.

protocol SuperPower{
    var power: String { get }
}

extension WonderWoman: SuperPower{
    var power: String { return "Super strength" }
}

extension TheFlash: SuperPower{
    var power: String { return "Fast running" }
}

As we can see the WonderWoman and TheFlash both are adopting the SuperPower protocol. The interesting point is the WonderWoman is derived from SuperHero. Then she adopts two more inheritance, causing multiple inheritance, through protocol, Cape and SuperPower.

Benefit of composition

The benefit is very strait forward. Composition removes the dependency thus breaks the code coupling. So use of composition will make the code flexible which results in easy code maintenance and modification.

Drawback of composition

If we start to use unnecessary composition then our code will hold too much abstraction resulting a fuzzy code base. We will loose the readability. So we always have to balance the composition usage. We will talk more about these topic on the next blog post.

End talk

As always the code is shared on GitHub.

So finally we introduce ourself with the tip of the protocol mountain. Theres a lot to go.
Stay tunes for the next blog post, where we will dive deep on the protocol oriented knowledge. Till then Happy talk.

4 thoughts on “Composition, breaker of chains

Leave a Reply

Notifications for mobidevtalk! Cool ;) :( not cool