Llegamos a nuestro último artículo sobre Go enfocado a programación orientada a objetos, en este artículo tocaremos un aspecto muy importante de la OO, nada más y nada menos que el polimorfismo y como utilizarlo en GO
¿Qué es el polimorfismo?
Cómo casi todos los términos que hay en programación parece algo más complicado de lo que es, pero una vez te lo explique verás que te queda muy claro e incluso puede que ya lo estuvieras aplicando sin ni siquiera conocer como se llamaba, esto suele pasar mucho en nuestro trabajo.
En programación orientada a objetos el polimorfismo se refiere a la posibilidad de tener distintas clases con métodos o atributos denominados de manera idéntica pero con comportamientos diferentes.
Esto esta genial, pero hemos dicho varias veces que en Go no hay clases, entonces, ¿cómo aplico el polimorfismo en Go?
Benditas interfaces
En Go sabemos que cualquier tipo que defina todos los métodos de una interfaz implementará ésta de manera implícita.
Para poder entender correctamente el polimorfismo en Go vamos a crear un pequeño programa que nos permita jugar con él.
Para ello vamos a crear un pequeño juego, con el reciente éxito en taquilla de los Vengadores: Endgame, tranquilos no haremos ningún spoiler, vamos a ver cuantas oportunidades tienen nuestros héroes de derrotar al temible Thanos
Empecemos con el polimorfismo en Go
Lo primero que haremos es crear una interfaz que tendrán que cumplir todas nuestras structs, está interfaz será army
.
type army interface {
name() string
power() int
}
Esta interfaz se compone de dos métodos, name()
que nos dará el nombre del ejército que se enfrentará a Thanos y power()
por el cual sabremos el poder de ataque.
Vamos a crear entonces nuestro primer ejército:
type nationalArmy struct {
armyName string
unitPower int
soldiers int
}
Nuestro nationalArmy
tiene un armyName
que nos dirá como se llama, unitPower
que será la cantidad de poder unitario de cada soldado y además podremos definir cuantos soldados tiene nuestro ejército con el campo soldiers
Pero sabemos que sólo con un ejército no haremos mucho frente a Thanos así que vamos a crear algo más:
type heroArmy struct {
heroName string
heroPower int
sideKicksPower int
}
Con este nuevo struct
tendremos a los héroes y sus ayudantes de nuestro lado en la lucha contra el terrible Thanos, aquí podremos definir el nombre del héroe con heroName
, su poder heroPower
y el poder de su o sus ayudantes sideKicksPower
.
¿Será esto suficiente para derrotar a Thanos? Para conocer esa respuesta tendremos que empezar a hacer uso del Polimorfismo así que para ello necesitamos que nuestros dos structs
cumplan con la interfaz army
. Cómo dijimos al principio del artículo para que un struct
cumpla con una interfaz, basta con implementar todos sus métodos, así que vamos allá.
func (n nationalArmy) name() string {
return n.armyName
}
func (n nationalArmy) power() int {
return n.unitPower * n.soldiers
}
func (h heroArmy) name() string {
return fmt.Sprintf("Army with %s", h.heroName)
}
func (h heroArmy) power() int {
return h.heroPower + h.sideKicksPower
}
Perfecto, ahora ya tenemos el modo en el cual cada ejército va a luchar, con lo cual sólo nos queda enfrentarnos al villano.
func fightAgainstThanos(armies []army) {
thanosLife := 9999999
for _, army := range armies {
fmt.Printf("%s fight against Thanos with a force of %d\n", army.name(), army.power())
thanosLife -= army.power()
}
if thanosLife <= 0 {
fmt.Println("The terrible Thanos has been defeated")
} else {
fmt.Printf("Thanos destroy entire universe, and his life is %d yet\n", thanosLife)
}
}
Como vemos en nuestro método fightAgainstThanos
estamos pasando un slice de interfaz army
, donde por cada iteración vemos el poder y nombre que tiene cada army
y el daño que le hace a Thanos. Y aquí gente es donde entra la magia del polimorfismo, ya que si mañana queremos añadir un nuevo ejército a efrentarse a Thanos simplemente tendremos que hacer que implemente nuestra interfaz army
y no tendremos que tocar el método fightAgainstThanos
para nada, ¿chulo no?
Vamos a ver cómo funciona el código actual y luego haremos la magia del polimorfismo, para que veáis que no todo son palabrerías.
func main() {
army1 := nationalArmy{"Air Forces", 10, 1000}
army2 := nationalArmy{"Marines", 20, 1000}
army3 := nationalArmy{"Space Forces", 40, 1000}
army4 := heroArmy{"Iron Man", 100000, 50000}
army5 := heroArmy{"Black Panther", 50000, 100000}
armies := []army{army1, army2, army3, army4, army5}
fightAgainstThanos(armies)
}
y el resultado de nuestra lucha sería:
Como dijimos vamos a ver la magia del Polimorfismo y meteremos una nueva variante en nuestra lucha, algo que cambiaría la balanza a nuestro favor.
type chuckArmy struct {
}
func (c chuckArmy) name() string {
return "The greatest Chuck Norris"
}
func (c chuckArmy) power() int {
return 9999999
}
Ya tenemos al gran Chuck Norris implementando nuestra interfaz army
tan sólo tendremos que añadirlo a los combatientes y ver que pasa:
func main() {
army1 := nationalArmy{"Air Forces", 10, 1000}
army2 := nationalArmy{"Marines", 20, 1000}
army3 := nationalArmy{"Space Forces", 40, 1000}
army4 := heroArmy{"Iron Man", 100000, 50000}
army5 := heroArmy{"Black Panther", 50000, 100000}
chuck := chuckArmy{}
armies := []army{army1, army2, army3, army4, army5. chuck}
fightAgainstThanos(armies)
}
El código completo lo podéis encontrar y jugar con él en el playground de Go: https://play.golang.org/p/ozTxlbO95Ad
Como veis el poder del polimorfismo es muy muy útil, ha conseguido algo que otros ven inviable. Y hasta aquí llegan nuestros artículos sobre Go orientado a objetos, ahora ya no tenéis excusa para empezar a aplicar todo lo aprendido en vuestro código.
Ya sabéis que cualquier duda o sugerencia nos la podéis dejar en los comentarios o a través de Twitter @FriendsofGoTech