Programación Orientada a Objetos (III)

En este artículo continuamos explorando el paradigma de programación orientada a objetos. En este caso vamos a hablar de la herencia entre clases. Se trata de un mecanismo que nos permite extender las propiedades o métodos de una clase en otra, sin tener que replicarlos. De este modo, podemos crear objetos (según nuestras necesidades), tanto de la clase original como de la clase derivada. También nos permite una mayor estructuración de nuestro código, optimizando la organización. Sin embargo, todas esas prestaciones no son gratuitas. El precio a pagar es que debemos dedicar mayor esfuerzo a definir la estructura interna de las clases, a fin de obtener un código limpio, mantenible y escalable. Vamos a ver qué es la herencia y cómo funciona.

DERIVANDO CLASES

Vamos a desarrollar un proyecto basado en el escenario que hemos estado empleando, en el que creamos objetos de tipo televisor. En este caso vamos a ampliar las funcionalidades de tales objetos. Para ello tenemos un directorio src/classes en el que tenemos dos clases. La primera es CrearTelevisorClass.php. Su código es el mismo que el del ejemplo del artículo anterior, por lo que no lo vamos a reproducir aquí.

La segunda clase está en el script TelevisorOperativoClass.php. Su código es el siguiente:

Veamos que tenemos aquí. En primer lugar nos vamos a fijar en la línea 5 del listado, que declara la clase:

class TelevisorOperativoClass extends CrearTelevisorClass

Fíjate que le hemos añadido la palabra reservada extends, y el nombre de la clase de la que hereda. Esto significa que la clase TelevisorOperativoClass va a tener, implícitamente, las propiedades y los métodos de la clase CrearTelevisorClass, sin necesidad de declararlos en la que estamos creando ahora.

Además, fíjate en el constructor de la clase que estamos creando. Recibe, como argumentos, los mismos que recibía la clase CrearTelevisorClass, además de otro argumento al que le establecemos un valor por defecto. Dentro del constructor llamamos, a su vez, al constructor de la clase padre, utilizando la palabra reservada parent::. Esto es muy habitual cuando usamos clases derivadas de otras. Si necesitamos usar un método que ya está en la clase padre, y no necesitamos cambiar su funcionamiento, no reescribimos el código, sino que llamamos al método de la clase padre, pasándole los argumentos necesarios.

El resto de los métodos de la clase derivada son operativas que le añadiremos a nuestro objeto, de modo que disponga de nuevas funcionalidades.

INSTANCIANDO LA CLASE DERIVADA

Al igual que antes, la mejor manera de ver como funciona es, por supuesto, hacerla funcionar. Observa el listado (listado inicial; luego lo ampliaremos) de index.php:

Observa que empezamos incluyendo los dos archivos de clase. Aunque el objeto lo vamos a crear a partir de la segunda clase (la nueva que hemos creado para este artículo), como esta, a su vez, deriva de la primera, necesitamos tener cargadas las dos. Si no, la herencia no funcionará.

Después creamos el objeto $miTele a partir de la clase TelevisorOperativoClass (la clase derivada). Entonces ocurren varias cosas: se invoca, automáticamente y de forma transparente, al constructor de la clase TelevisorOperativoClass, pasándole los argumentos $color, $pulgadas y $pantalla. El constructor de esta clase espera, además, un cuarto argumento, llamado $encendido pero, cómo este ya tiene un valor por defecto en la declaración, podemos omitirlo y se asumirá dicho valor por defecto.

Dentro del constructor se llama al constructor de la clase padre, pasándole los tres primeros argumentos, que son los que éste debe recibir. Lo hacemos con la línea siguiente:

parent::__construct($color, $pulgadas, $pantalla);

En este momento tenemos un objeto de la clase derivada que, además, tiene establecidas las propiedades que se han heredado de la clase padre. Como el constructor de la clase derivada establece, además, tres propiedades que se declaran en esta, el objeto ya tiene seis propiedades. Cuando hacemos el volcado del objeto en pantalla, vemos lo siguiente:

object(TelevisorOperativoClass)#1 (6) {
  ["encendido":"TelevisorOperativoClass":private]=>
  bool(false)
  ["volumen":"TelevisorOperativoClass":private]=>
  int(0)
  ["canal":"TelevisorOperativoClass":private]=>
  int(1)
  ["color":"CrearTelevisorClass":private]=>
  string(5) "negro"
  ["pulgadas":"CrearTelevisorClass":private]=>
  string(2) "42"
  ["pantalla":"CrearTelevisorClass":private]=>
  string(4) "OLED"
}

Observa que tenemos un objeto de la clase TelevisorOperativoClass, con 6 propiedades, como nos informa la primera línea. Aunque la clase hereda de otra, el objeto es de la clase derivada, no de la clase padre, porque así lo hemos indicado en el código. Sin embargo, observa la matriz de propiedades. En los índices se especifica qué propiedades se han creado en la clase derivada, y cuáles han sido heredadas de la clase padre. El objeto, no obstante, tiene todas las propiedades, con independencia de donde se hayan declarado. Si el objeto se hubiera creado desde la clase padre, sólo tendría las propiedades de esta, no las de la clase derivada.

USANDO EL OBJETO

Vamos a ampliar el script index.php, de modo que veremos que, no solo se han heredado las propiedades, sino también los métodos. La nueva versión es la siguiente:

Cuando ejecutes este código, verás, en la pantalla de tu navegador, lo siguiente:

El color original del televisor es negro.
El nuevo color del televisor es azul.
Con el televisor apagado no se puede subir el volumen.
El televisor está encendido.
El volumen está en nivel 1.
El canal actual es 35.

Siguiendo los comentarios que te he añadido en el código verás que no tienes ningún problema para entender cómo está funcionando.

HERENCIA MÚLTIPLE

Ya hemos mencionado que PHP (entre otros lenguajes) no admite la herencia múltiple. Existen otros lenguajes, como Python, que sí la soportan. El concepto de herencia múltiple es que una clase pueda derivar de dos o más clases simultáneamente. Vamos a pensar en nuestro objeto de tipo Televisor, al que ahora queremos añadirle una clase que permita integrar un vídeo. A la nueva clase la vamos a llamar TelevisorConVideoClass. Si quisiéramos que esta heredara de CrearTelevisorClass y TelevisorOperativoClass, en un lenguaje que soportara la herencia múltiple, la declaración de la clase sería algo así:

class TelevisorConVideo extends CrearTelevisorClass, TelevisorOperativoClass

Lo que, a su vez, nos obligaría a redefinir las dos clases anteriores, para que no hubiera herencia entre ellas. Pero, cómo PHP no soporta la herencia múltiple, esto dará un error de sintaxis.

Sin embargo, hay una manera de obtener el mismo resultado, sin romper las reglas de PHP (sólo «retorciéndolas» un poco, en nuestro provecho). Cómo quiera que TelevisorOperativoClass ya está heredando de CrearTelevisorClass, si hacemos que TelevisorConVideoClass herede de TelevisorOperativoClass, está, realmente, heredando de las dos clases. Así estamos obteniendo lo más parecido que podemos a la herencia múltiple. La clase TelevisorConVideoClass podría quedar como vemos a continuación:

Entonces modificamos el archivo index.php para que el objeto $miTele sea creado a partir de TelevisorConVideoClass, así:

En la pantalla del navegador obtenemos, ahora, lo siguiente:

object(TelevisorConVideoClass)#1 (7) {
  ["videoConectado":"TelevisorConVideoClass":private]=>
  bool(false)
  ["encendido":"TelevisorOperativoClass":private]=>
  bool(false)
  ["volumen":"TelevisorOperativoClass":private]=>
  int(0)
  ["canal":"TelevisorOperativoClass":private]=>
  int(1)
  ["color":"CrearTelevisorClass":private]=>
  string(5) "negro"
  ["pulgadas":"CrearTelevisorClass":private]=>
  string(2) "42"
  ["pantalla":"CrearTelevisorClass":private]=>
  string(4) "OLED"
}
El color original del televisor es negro.
El nuevo color del televisor es azul.
Con el televisor apagado no se puede subir el volumen.
El televisor está encendido.
El volumen está en nivel 1.
El canal actual es 35.
El vídeo está conectado.

Cómo puedes ver, si lees los comentarios del código, se están usando métodos de las tres clases, de forma que tenemos el mismo resultado que si hubiéramos contado con la herencia múltiple.

CONCLUYENDO

En este artículo hemos aprendido lo necesario de la herencia de clases, y cómo funcionan los objetos de clases derivadas. En el próximo artículo hablaremos de propiedades y métodos estáticos, y de sobrescritura y sobrecarga de métodos. Los ejemplos finales de este artículo los tienes en este enlace.

   

Deja un comentario