Brendon Cheung
Brendon Cheung

Reputation: 1035

Different between Protocol Oriented Programming and Object Oriented Programming with examples

Recently I came across protocol oriented programming (WWDC 2015) and it is really fascinating but yet it is a concept that is very difficult to tame and put it into good practice. So I did some research on the internet and came across this code. It models a player and enemy in a typical game scene, where the player can equip a weapon and shoots an enemy, then the enemy subsequently takes damage:

protocol Targetable {
    var life: Int { get set }
    func takeDamage(damage: Int)
}

protocol Shootable {
    func shoot(target: Targetable)
}

class Pistol: Shootable {
    func shoot(target: Targetable) {
        target.takeDamage(1)
    }
}

class Shotgun: Shootable {
    func shoot(target: Targetable) {
        target.takeDamage(5)
    }
}

class Enemy: Targetable {
    var life: Int = 10

    func takeDamage(damage: Int) {
        life -= damage
        println("enemy lost \(damage) hit points")

        if life <= 0 {
            println("enemy is dead now")
        }
    }
}

class Player {
    var weapon: Shootable!

    init(weapon: Shootable) {
        self.weapon = weapon
    }

    func shoot(target: Targetable) {
        weapon.shoot(target)
    }
}

var terminator = Player(weapon: Pistol())

var enemy = Enemy()

terminator.shoot(enemy)
//> enemy lost 1 hit points 

To me, this makes a lot of sense. But, at the back of my mind, I told myself "Yea, this make sense but if I were to implement something like this, it would be entirely different", and so I did:

class Gun {
    var damage: Int {
        return 0
    }
}

class Pistol: Gun {
    override var damage: Int {
        return 5
    }
}

class Shotgun: Gun {
    override var damage: Int {
        return 10
    }
}

class Enemy {

    var health = 100

    func takeDamage(damage: Int) {
        health = health - damage
        print("Current health is: \(health)")
    }

    init(health: Int) {
        self.health = health
    }
}

class Player {

    var gun: Gun

    func shoot(enemy: Enemy) {
        enemy.takeDamage(damage: gun.damage)
    }

    init(gun: Gun) {
        self.gun = gun
    }
}

let player = Player(gun: Pistol())
let enemy = Enemy(health: 100)

player.shoot(enemy: enemy)
//Current health is: 95

They both model share the same damage-taking mechanism, you have one approach that is a mixture of protocols and object, and on the other hand you have pure objects. Can many one tell me which is the best approach and why?

Thanks!

Upvotes: 0

Views: 952

Answers (2)

KimNguyen
KimNguyen

Reputation: 1791

One of the advantages of POP is that a class/struct can adopt more than one protocol, whereas a class can be a subclass of just one superclass. In the example above if you want that a Player can also be shooted (by the enemy or another player), you just need to make Player class to conform to Targetable protocol

class Player: Targetable { ... }

Second, once you use a Protocol as Type, a variable of that Type can be an instance of any Class that complies with this Protocol. In the example above and in the real world, the weapon of the Player can be shotgun or pistol, and can be changed at any time.

var terminator = Player(life: 100, weapon: Pistol())
var enemy = Enemy()

terminator.shoot(enemy)

//> enemy lost 1 hit points

terminator.weapon = Shotgun()
terminator.shoot(enemy)

//> enemy lost 5 hit points

They are just a few points to showcase the advantages of POP, there should be more in more specific cases.

Upvotes: 1

Charles Srstka
Charles Srstka

Reputation: 17060

Which is better is a subjective question, which to my knowledge is not encouraged on this site. However, the first one fits more within the "protocol-oriented" idiom which has been recommended by the Swift community as of late.

However, one thing that sticks out about your second example is that your Gun superclass does not do much other than define an interface; its own implementation of damage is never actually used. Therefore, a protocol certainly seems the correct approach to take in this case.

Upvotes: 2

Related Questions