Friday, July 27, 2018

Protocols vs Base classes


Ever got confused with using Base classes modal or using Protocols while developing app with Swift?


Base class:


       
 class Employee {
    func work() {}
 }

 class Manager {
 }



Protocol:


       
 protocol Works {
    func work()
 }

 extension Works {
    func work() {}
 }

 class Manager: Works {
 }



As it seems like the same thing can achieved using Base classes, Then why to go for Protocols?


In this tutorial, let’s discuss when to go for Base classes and when to go for Protocols

I have an Employee base class and below child classes derived from Employee class.

  • Programmer
  • TeamLead
  • ProjectLead
  • Manager


       
 class Employee {
    func work() {}
 }

 class Programmer: Employee {}

 class TeamLead: Employee {}

 class Lead: Employee {
    func trainTeam() {}
 }

 class ProjectLead: Employee {
    func trainTeam() {}
 }

 class Manager: Employee {}




Here, both TeamLead and ProjectLead are capable of training the team and it’s a redundant code and redundant code is not encouraged.

Two things we can do here.

1) We can keep the redundant code in Base class.

       
 class Employee {
    func work() {}
    func trainTeam() {}
 }

 class Programmer: Employee {}

 class TeamLead: Employee {}

 class ProjectLead: Employee {}

 class Manager: Employee {}



All good now?

I say NO. Because, even Programmer can access trainTeam() which he can’t with his experience and also Manager can access which is not his/her responsibility though they can.

       
 let programmer = Programmer()
 programmer.trainTeam()

 let manager = Manager()

 manager.trainTeam()



Agree?



2) We can write a separate class called Trainer and derive both TeamLead and ProjectLead from it.

       
 class Employee {
    func work() {}
 }

 class Programmer: Employee {}

 class Trainer: Employee {
    func trainTeam() {}
 }

 class TeamLead: Trainer {}

 class ProjectLead: Trainer {}

 class Manager: Employee {}

 let tl = TeamLead()
 tl.trainTeam()

 let pl = ProjectLead()
 pl.trainTeam()




Looks good. But it’s not scalable.

What If ProjectLead and Manager has unique feature called promote() ?

       
 class Employee {
    func work() {}
 }

 class Programmer: Employee {}

 class Trainer: Employee {
    func trainTeam() {}
 }

 class TeamLead: Trainer {}

 class ProjectLead: Trainer {
    func promote() {}
 }

 class Manager: Employee {
    func promote() {}
 } 




Again there is redundant code(promote()). Now we need again a base class like Promoter.

       
 class Employee {
    func work() {}
 }

 class Programmer: Employee {}

 class Trainer: Employee {
    func trainTeam() {}
 }

 class Promoter: Employee {
    func promote() {}
 }

 class TeamLead: Trainer {}

 class ProjectLead: Promoter {}

 class Manager: Promoter {}

 let pl = ProjectLead()
 pl.promote()



Both Manager and ProjectLead can be derived from Promoter class. Manager is fine but ProjectLead loses the trainTeam() functionality here cause he is now derived from Promoter class instead from Trainer class.

And we in Swift, a class can’t be derived from multiple classes. It gives below build error.

       
 // Build Error :- Multiple inheritance from 
    classes 'Promoter' and 'Trainer'

    class ProjectLead: Promoter, Trainer {}



This is the design issue arises with classes hierarchy. 

Let’s see how this can be achieved using protocol oriented approach.

Classes represents objects itself.
Protocols represents object’s behaviour.

If we separate employee’s behaviour/actions as per above example.

  • works
  • promotes
  • trains

Based on this let’s take protocols.

       
 protocol Worker {
    func work()
 }

 extension Worker {
    func work() {}
 }

 protocol Trainer {
    func trainTeam()
 }

 extension Trainer {
    func trainTeam() {}
 }

 protocol Promoter {
    func promote()
 }

 extension Promoter {
    func promote() {}
 }



Now it’s very easy to build concrete complex classes.

       
 class TeamLead: Worker, Trainer {}

 class ProjectLead: Worker, Trainer, 
                                              Promoter {}

 class Manager: Worker, Promoter {}



We don’t have redundant code here cause of protocol extensions which implemented default behaviour.

Now my design is scalable, I can create any type of concrete class now.

Let’s say we need to create a position called AssistantManager who can’t train but can promote and ProjectLead can’t promote.

       
 class ProjectLead: Worker, Trainer {}

 class AssistantManager: Worker, Promoter {}



As simple as that. That’s how our design with protocol oriented approach is scalable and helps in Multiple Inheritance.

We need to take a call in choosing b/w Base classes approach and Protocol oriented approach based on the scalability of our requirements.

Hope this post is useful. Feel free to comment incase of any queries.