Polimorfismo y #[\Override]
Hay una propiedad, atributo o característica importante introducida finalmente en la versión de PHP 8.3 llamada “override” que nos facilita el proceso de sobrescribir métodos.
Realmente se escribe #[\Override] y se puede usar tanto en clases, interfaces, enums, traits y clases que implementan interfaces. Su uso es más bien estético, ya que no afecta al comportamiento de la clase y es más bien como una ayuda para el desarrollador.
Para entenderlo un poco más, vamos a recordar dos conceptos fundamentales de la programación orientada a objetos, la herencia y el polimorfismo:
- Herencia: Hablamos de herencia en la programación orientada a objetos cuando una clase (clase hija) adquiere (hereda) las propiedades y métodos de otra clase (clase padre), facilitando la creación de nuevas clases basadas en clases ya existentes.
- Polimorfismo: Capacidad de un objeto de actuar de diferentes maneras, es decir, un método de una clase hija, tiene la posibilidad de modificar su comportamiento sobrescribiendo dicho método, o en otras palabras, se trata de sobrescribir un método en una clase hija, permitiendo que la llamada a este método se comporte de manera diferente dependiendo del objeto (o clase) que lo ejecute. Y es aquí, donde entra en juego “#[\Override]”.
Imagínate el siguiente error que, aunque parezca que no, a veces con las prisas se da con frecuencia, que es, escribir un nombre de método incorrecto en la clase hija, dando lugar a comportamientos inesperados sin que el desarrollador se percate.
Mira el siguiente ejemplo:
<?php class Animal { public function hacerSonido() { echo "Sonido genérico"; } } class Perro extends Animal { public function hacerSonido() { echo "Guau, guau!"; } } class Gato extends Animal { public function hacerSonid() { echo "Miau, miau!"; } } function reproducirSonido(Animal $animal) { $animal->hacerSonido(); } $miPerro = new Perro(); $miGato = new Gato(); reproducirSonido($miPerro); reproducirSonido($miGato);
Si ejecutamos el ejemplo anterior, a la hora de llamar a “reproducirSonido($miGato)”, el sonido que se producirá es un sonido genérico porque a la función reproducirSonido(), el animal que se le pasa es de tipo gato, si, pero… ¡no contiene ningún método llamado hacerSonido()! Sino “hacerSonid()”.
Como ves, puede ser un error tonto, pero difícil de detectar.
Si ahora aplicamos “#[\Override]”:
<?php class Animal { public function hacerSonido() { echo "Sonido genérico"; } } class Perro extends Animal { #[\Override] public function hacerSonido() { echo "Guau, guau!"; } } class Gato extends Animal { #[\Override] public function hacerSonid() { echo "Miau, miau!"; } } function reproducirSonido(Animal $animal) { $animal->hacerSonido(); } $miPerro = new Perro(); $miGato = new Gato(); reproducirSonido($miPerro); reproducirSonido($miGato);
Y ejecutamos el ejemplo, nos mostrará un error similar a:
Fatal error: Gato::hacerSonid() has #[\Override] attribute, but no matching parent method exists in C:\laragon\www\PHP\temas blog\ejemplos\override\override.php on line 23
Como ves, ya nos esta indicando que no existe un método en la clase padre que coincida con “hacerSonid()”.
Así que, básicamente, #[Override] nos ayuda como desarrolladores haciendo que el compilador de PHP se aseguré de que se esta sobrescribiendo un método en la clase hija que realmente existe en la clase padre. Sino existe en la clase padre, PHP lanzará un error ayudándonos de esta manera a identificar el problema antes de que se conviertan en errores en tiempo de ejecución.
En el ejemplo anterior, la propiedad #[\Override] asegura que los métodos hacerSonido() en Perro y Gato estén realmente sobrescribiendo el método de Animal, en caso de que escribamos mal el nombre o no haya un método correspondiente en la clase padre, PHP genera un error.
Como has visto, el polimorfismo es esencial en la programación orientada a objetos, y la propiedad #[\Override] está directamente relacionada con él. Cuando usamos #[\Override], no solo nos aseguramos de aplicar correctamente el polimorfismo, sino que también hacemos el código más claro y menos propenso a errores.
Por último y para finalizar, aquí dejo una página que incluye varios ejemplos aplicados a interfaces, enums, traits, etc.