Reputation: 1035
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
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
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