En este artículo tratamos dos temas de suma importancia a la hora de trabajar con métodos en clases, sobre todo, cuando hay herencia de por medio: la sobrecarga de métodos, y la sobrescritura de los mismos.
Con estas técnicas, veremos como flexibilizar más aún el uso de métodos en clases, así cómo sacarles mayor partido, pudiendo hacer que su comportamiento varíe, manteniendo o alterando el código a nuestra conveniencia.
El uso de estas técnicas supone un avance sustancial en la línea de flexibilizar nuestro código, manteniendo su legibilidad y optimización, por lo que son de suma importancia en la programación actual, como podrás comprobar cuando sigas leyendo.
SOBRECARGA DE MÉTODOS
Se conoce como sobrecarga de métodos el hecho de que un método de una clase tenga distintos comportamientos según el número o tipo de argumentos que se le pasen. Así, el mismo método puede usarse para distintas finalidades, pasándole una lista de argumentos u otra. Ojo con esto. Esta técnica puede parecer una especie de panacea para que nuestra clase tenga un sólo método que sirva «para todo». Nada más lejos de la realidad. Debemos ser coherentes con la programación que realizamos. Cuando decimos que un método «puede tener distintas finalidades» nos estamos refiriendo al hecho de que puede cumplir objetivos muy similares o relacionados entre sí, o que puede llevar a cabo una misma tarea de distintos modos. Un viejo adagio dice que lo que sirve para todo no vale para nada.
En muchos lenguajes se obtiene la sobrecarga de métodos declarando varios métodos con el mismo nombre, pero con distintas líneas de parámetros. Si has leído algo sobre Java o C++ te resultará familiar algo como esto:
1 2 3 4 5 6 7 8 9 10 11 12 |
public static MetodoDelUsuario (String arg1){ // Acciones a realizar si el método recibe un argumento de tipo String } public static MetodoDelUsuario (String arg1, String arg2){ // Acciones a realizar si el método recibe dos argumentos de tipo String } public static MetodoDelUsuario (String arg1, int arg2){ // Acciones a realizar si el método recibe dos argumentos, // el primero de tipo String y el segunto de tipo int. } |
Esto en PHP no funciona así. De hecho, este lenguaje emplea una solución más simple y, a mi entender, mucho más elegante. Dentro del cuerpo de una función (que es, en definitiva, un método) existen funciones propias del lenguaje que pueden determinar los argumentos que ha recibido, así como el tipo o el valor de los mismos. Las funciones de PHP que nos interesan son:
func_get_args
. Devuelve una matriz con los argumentos que ha recibido la función.func_get_arg
. Devuelve el valor de un argumento específico de la función.func_num_args
. Devuelve el número total de argumentos que ha recibido la función.gettype
. Devuelve el tipo de un dato.
En realidad, PHP cuenta con más funciones para el control de la operativa de funciones definidas por el desarrollador, que puedes conocer, si lo deseas, en este enlace. Sin embargo, las cuatro que te comento en este artículo (sobre todo las tres primeras), son lo que nos permite crear la sobrecarga de métodos en PHP.
Vamos a ver como operan, mediante un ejemplo muy simple. Empleamos una variante de la clase CrearTelevisorClass.php
, que tiene un método sobrecargado, como ves a continuación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
<?php /* Declaramos la clase que crea los televisores. Es una buena práctica de programación que las clases estén definidas en archivos individuales, y que cada archivo tenga el mismo nombre que la clase que se declara en él. */ class CrearTelevisorClass { /* Declaramos las variables como privadas. De este modo, nos aseguramos la encapsulación. */ private static $color = "negro"; private $pulgadas; private $pantalla; private $encendido; private $canal; /* El constructor crea un objeto y le asigna valores a las variables del mismo, que también son privadas. */ public function __construct($pulgadas, $pantalla) { $this->pulgadas = $pulgadas; $this->pantalla = $pantalla; $this->encendido = false; } /* Creamos los métodos setter para modificar las propiedades de un objeto. */ public static function setColor($color) { CrearTelevisorClass::$color = $color; } public function setPulgadas($pulgadas) { $this->pulgadas = $pulgadas; } public function setPantalla($pantalla) { $this->pantalla = $pantalla; } /* Creamos los métodos getter para recuperar el valor de las propiedades de un objeto. */ public static function getColor() { return CrearTelevisorClass::$color; } public function getPulgadas() { return $this->pulgadas; } public function getPantalla() { return $this->pantalla; } /* Creamos un método sobrecargado. Cuando se crea un método así, se declara sin parámetros, porque es dentro del cuerpo del método donde detectaremos que argumentos recibe, cuales son sus valores (y sus tipos, si nos hace falta), y obraremos en consecuencia. */ public function manejarTelevisor() { /* Si no le pasamos argumentos, nos da un mensaje al respecto, y sale del método. */ if (func_num_args() == 0){ echo "Has cogido el mando, pero no has pulsado ningún botón.<br>"; return; } /* Si el primer argumento no es de tipo boolean, no hace nada. Nos informa de un error y sale del método. Si es de tipo boolean, "enciende" o "apaga" el televisor, y nos informa del estado actual. */ if (gettype(func_get_arg(0)) != "boolean"){ echo "Has pulsado el botón de encender y apagar, pero no funciona.<br>"; return; } else { $this->encendido = func_get_arg(0); echo ($this->encendido)?"El televisor está encendido.<br>":"El televisor está apagado.<br>"; } /* Se comprueba que exista un segundo argumento, y que el televisor esté "encendido". Si no es el caso, se abandona el método. */ if (func_num_args() == 1 || $this->encendido === false) return; /* Si el segundo argumento no es numérico, nos informa de un error y abandona el método. */ if (gettype(func_get_arg(1)) != "integer"){ echo "Has pulsado un botón de canal, pero no funciona.<br>"; return; } /* Si el segundo argumento es numérico, se cambia el canal, y se nos informa del canal actual. */ $this->canal = func_get_arg(1); echo "El canal seleccionado es ".$this->canal.".<br>"; } } ?> |
Analiza el código resaltado, donde he incluido los comentarios necesarios para que veas como funciona. A continuación te reproduzco el archivo index.php
, donde creamos un objeto de la clase CrearTelevisorClass
, y llamamos al método sobrecargado, sin pasarle ningún argumento.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?php /* Se invoca el script que tiene la clase que vamos a necesitar. */ include ("src/classes/CrearTelevisorClass.php"); /* Creamos un objeto llamado $miTele, usando el operador new y el nombre de la clase que queremos instanciar. Esto invoca, de forma transparente, al constructor de la clase, al que le pasa los argumentos que especificamos en la instanciación. */ $miTele = new CrearTelevisorClass("42", "OLED"); /* Si llamamos al método $miTele->manejarTelevisor() sin ningún argumento, nos sale un mensaje de aviso de que no le estamos pasando datos para hacer nada. */ $miTele->manejarTelevisor(); ?> |
Como vemos en la definición de la clase, si el valor de func_num_args()
es 0
(como ocurre en este caso), se nos da un mensaje informando de que hay un error, y se abandona el método. Esto es lo que vemos en la pantalla del navegador:
Has cogido el mando, pero no has pulsado ningún botón.
Ahora vamos a ver una variante del uso del método, en la que le pasamos el primer argumento, con el valor true
, para «encender» el televisor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?php /* Se invoca el script que tiene la clase que vamos a necesitar. */ include ("src/classes/CrearTelevisorClass.php"); /* Creamos un objeto llamado $miTele, usando el operador new y el nombre de la clase que queremos instanciar. Esto invoca, de forma transparente, al constructor de la clase, al que le pasa los argumentos que especificamos en la instanciación. */ $miTele = new CrearTelevisorClass("42", "OLED"); /* Si llamamos al método $miTele->manejarTelevisor() con un argumento, el primero (único, en este caso) debe ser boooleano. Le pasamos true, para "encender" el televisor. */ $miTele->manejarTelevisor(true); ?> |
El resultado en pantalla es, como cabría esperar, el siguiente:
El televisor está encendido.
Sin embargo, no nos dice nada relativo al canal, porque no le hemos pasado el segundo argumento. En index.php
sustituye la línea:
$miTele->manejarTelevisor(true);
por
$miTele->manejarTelevisor(true, 10);
El resultado en pantalla es:
El televisor está encendido.
El canal seleccionado es 10.
De esta forma, un mismo método opera de diferente manera, según los argumentos que le hayamos pasado. Ahora es cuestión de que pruebes cambiando los valores (e, incluso, los tipos de datos que le pasas), para que te familiarices con esta técnica.
SOBRESCRITURA DE MÉTODOS
Esta es una técnica que se emplea cuando recurrimos a la herencia entre clases. Consiste en que, en la clase derivada, creamos un método con el mismo nombre que uno ya existente en la clase padre, pero con diferente operativa. De este modo, si el objeto lo declaramos de la clase padre, el método actuará como se define en esta clase. Sin embargo, si el objeto lo declaramos cono de la clase derivada, el método actuará como se define en esta última.
Puede que aún no hayas reparado en ello, pero esta es una técnica tan habitual que, sin proponérnoslo ya la hemos usado, con el constructor en este artículo. Del mismo modo, se puede emplear con cualquier otro método que necesitemos. Como tienes los ejemplos a tu disposición, no vamos a repetirnos aquí.
CONCLUYENDO
En este artículo hemos visto la sobrecarga de métodos en PHP, y nos hemos dado cuenta de que ya conocemos, también, la sobrescritura de métodos. Como siempre, aquí te dejo el enlace para descargar los ficheros de este artículo. En el próximo hablaremos de las limitaciones a la herencia, mediante lo que se conoce como finalización.