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
- What is composition
- Protocol, Swift way of Composition
- Example
- Explanation of the Example
- Multiple Inheritance
- Benefit of composition
- Drawback of composition
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”