martes, 22 de enero de 2013

Herencia en PHP

La herencia es una propiedad de la programación orientada a objetos (POO), no únicamente de PHP.
La herencia nos permite utilizar una clase como base para otra, y asi podemos extender sus funcionalidades, o diseñar de una manera mejor nuestro sistema.

Por ejemplo, vamos a definir la clase persona de la siguiente manera:
class Persona {
	var $nombre;
	var $apellido;
	var $dni;
	var $calle;
 
 
	function __construct($nombre, $apellido, $dni, $calle) {
		print "Creando objecto de la clase Persona con nombre completo: " .
			   $nombre . " " . $apellido . " y DNI: " . $dni . "\n";
		$this->nombre = $nombre;
		$this->apellido = $apellido;
		$this->dni = $dni;
		$this->calle = $calle;
	}

	function mostrarDatos(){
		print "Nombre: " .  $this->nombre .  "\n";
		print "Apellido: " .  $this->apellido .  "\n";
		print "DNI: " .  $this->dni . "\n";
		print "Calle: " .  $this->calle . "\n\n";
	}
}
Como podemos ver, la clase persona tiene 4 atributos: nombre, apellido, dni y calle; ademas de la funcion mostrarDatos.

Ahora imaginemos que queremos construir un sistema en el cual tenemos compradores y vendedores.
Ambos heredaran de persona, pero cada clase tendrá sus propias propiedades:
  • Los vendedores tienen un atributo especifico que undica su número de vendedor. También pueden vender productos, cosa que no pueden los compradores.
  • A su vez, los compradores pueden comprar productos y no tienen número de vendedor, ni nada.

Estas clases quedarían definidas de la siguiente manera:

class Vendedor extends Persona {
	var $identificadorVendedor;

	function __construct($nombre, $apellido, $dni, $calle, $identificadorVendedor) {
		parent::__construct($nombre, $apellido, $dni, $calle);
		$this->identificadorVendedor = $identificadorVendedor;
		print  $nombre . " es un VENDEDOR \n";
	}

	function vender() {
		print "Vendedor " .  $this->nombre . " con numero: " . $this->identificadorVendedor . ", vende un producto \n";
	}
}

class Comprador extends Persona {
	
	function comprar() {
		print "Comprador " .  $this->nombre . " compra un producto\n";
	}
}
Ahora que tenemos nuestras clases definidas vamos a probar a crear un par de vendedores y compradores, y ver que podemos hacer con ellos.
$pedro = new Vendedor ("Pedro", "Sanchez", "40123903K", "Calle Falsa 123", "4214");
$juan = new Comprador("Juan", "Gabilondo", "10392129E", "Calle Loca n: 2");

$pedro->vender();
$juan->comprar();

La salida que obtenemos es la siguiente:
Creando objecto de la clase Persona con nombre completo: Pedro Sanchez y DNI: 40123903K
Pedro es un VENDEDOR 
Creando objecto de la clase Persona con nombre completo: Juan Gabilondo y DNI: 10392129E
Vendedor Pedro con numero: 4214, vende un producto 
Comprador Juan compra un producto

Podemos observar varias cosas:
  1. Desde el constructor de Vendedor hemos llamado al constructor del padre (clase Persona) mediante: parent::__construct($nombre, $apellido, $dni, $calle); 
  2. Que en el constructor de Vendedor, aparte de la llamada al padre hemos asignado el atributo identificadorVendedor, y hemos puesto una salida por pantalla.
  3. Cuando hemos definido la clase Comprador no hemos implementado el constructor. No ha hecho falta, pues no queriamos un comportamiento diferente al del padre (clase Persona). Y como podemos observar ha funcionado.
Podríamos probar a intentar llamar a la funcion vender desde un comprador, pero no daria ninguna salida.

Lo que si podemos hacer es llamar a los metodos del padre.
$pedro->mostrarDatos();
$juan->mostrarDatos();

Nos daría la siguiente salida:
Nombre: Pedro
Apellido: Sanchez
DNI: 40123903K
Calle: Calle Falsa 123

Nombre: Juan
Apellido: Gabilondo
DNI: 10392129E
Calle: Calle Loca n: 2

Se puede ver que de Pedro, que es vendedor, no nos apare su número de vendedor.
Si quisieramos que saliese este dato deberíamos sobreescribir la funcion mostrarDatos en la clase Vendedor. Sobreescribir consiste en escribir exactamente la misma funcion en la clase hija.

Quedaría de la siguiente manera:
class Vendedor extends Persona {
	var $identificadorVendedor;

	function __construct($nombre, $apellido, $dni, $calle, $identificadorVendedor) {
		parent::__construct($nombre, $apellido, $dni, $calle);
		$this->identificadorVendedor = $identificadorVendedor;
		print  $nombre . " es un VENDEDOR \n";
	}

	function vender() {
		print "Vendedor " .  $this->nombre . " con numero: " . $this->identificadorVendedor . ", vende un producto \n";
	}

	function mostrarDatos(){
		print "Nombre: " .  $this->nombre .  "\n";
		print "Apellido: " .  $this->apellido .  "\n";
		print "DNI: " .  $this->dni . "\n";
		print "Calle: " .  $this->calle . "\n";
		print "Numero de vendedor: " . $this->identificadorVendedor ."\n\n";
	}
}

Si ejecutamos mostrarDatos con Pedro y Juan, podemos ver que la salida cambia para Pedro (que es vendedor), pero no para Juan (que es comprador).
Nombre: Pedro
Apellido: Sanchez
DNI: 40123903K
Calle: Calle Falsa 123
Numero de vendedor: 4214

Nombre: Juan
Apellido: Gabilondo
DNI: 10392129E
Calle: Calle Loca n: 2

Podemos descargar los códigos de ejemplo en los siguientes enlaces:

Clases, Objetos y Constructores en PHP


Para entender los constructores hay que tener claro el concepto de clase y objeto.

No voy a utilizar definiciones formales, ni voy a referenciar a otros sitios. Si no queda claro, siempre se puede preguntar e intentare aclararlo cuando sea posible.

CLASES

Una clase es la definición de como son las cosas.
Por ejemplo, podemos tener una clase Persona, a la cual le asignaremos una altura y un nombre.
Quedaría definida tal que así:
class Persona {
   var $nombre;
   var $altura;
}

Esta es la definición básica de una clase persona.
A cada variable interna que creamos la llamaremos atributos. Así pues Persona tiene dos atributos: nombre y altura.

 Ahora pasaremos a ver los objetos y su relación con las clases.

OBJETOS

Los objetos son cosas concretas de un determinado tipo. Al tipo al que nos referimos será la clase.
Por ejemplo, tenemos la clase persona, así que ahora podemos definir diferentes objetos de esa clase.

$juan = new Persona;
$jesus = new Persona;
$ana = new Persona;

Acabamos de crear 3 objetos de tipo Persona (también se puede decir que hemos creado 3 instancias de la clase Persona).

Ahora podríamos acceder a ellos y asignarles diferentes valores a sus atributos.
Por ejemplo:

$juan->nombre = "Juan";
$juan->altura = 182;

$jesus->nombre = "Jesus Alfonso";
$jesus->altura = 168;

$ana->nombre = "Ana";
$ana->altura = 162;
No debemos confundir el nombre de la variable de los objetos, en este caso: $juan, $jesus y $ana con el atributo nombre que le hemos dado a esta clase "Juan", "Jesus Alfonso", "Ana".
Ni es obligatorio que una clase tenga atributo nombre, ni este ha de ser el mismo que el de la variable. Simplemente debemos nombrar la variable de la manera que mejor nos recuerde que contiene.

Podemos tambien imprimir los datos de los objetos:
print "Nombre: " . $juan->nombre . ", altura: ". $juan->altura;
print "Nombre: " . $jesus->nombre . ", altura: ". $jesus->altura;
print "Nombre: " . $ana->nombre . ", altura: ". $ana->altura;

CONSTRUCTORES

A la hora de crear objetos nos puede venir bien tener constructores, que son funciones que se ejecutan al crear el objeto de una clase.

Los podemos utilizar para inicializar valores de la clase, enlazar el objeto con otros objetos, mostrar mensajes por pantalla, etc. para lo que queramos básicamente.
Para crear un constructor hay que definir una función llamada __construct en la cual definiremos lo que vamos a hacer.
En la clase Persona que vimos anteriormente vamos a crear un constructor que nos muestre un mensaje por pantalla e inicialice la altura y nombre de los objetos que creemos posteriormente.

class Persona {
 var $nombre; // esto es un atributo
 var $altura; // esto es un atributo

 function __construct($nombre, $altura){ // este es el constructor
  print "Creando objecto de la clase Persona con nombre " . 
    $nombre . " y altura " . $altura . "cm";
  $this->nombre = $nombre;  
  $this->altura = $altura;
 }
}
Nota: Como podemos ver, hacemos uso de la variable $this. Esta variable nos sirve para acceder a los parámetros y funciones de la clase.

En este caso accedemos al parámetro nombre y altura.

Gracias al constructor ahora podemos crear objetos de manera más rápida.
$juan = new Persona("Juan", 182);
$jesus = new Persona("Jesus Alfonso", 168);
$ana = new Persona("Ana", 162);

Ahora ya podríamos acceder a estos objetos, y a sus variables.
print "Nombre: " . $juan->nombre . ", altura: ". $juan->altura;
print "Nombre: " . $jesus->nombre . ", altura: ". $jesus->altura;
print "Nombre: " . $ana->nombre . ", altura: ". $ana->altura;
Nota: En este caso no se hace uso de la variable $this, para acceder a los parámetros, sino que se utiliza el nombre del objeto en concreto. La variable $this se utiliza sólo dentro de la definición de la clase.

La salida que obtenemos es la siguiente:

Creando objecto de la clase Persona con nombre Juan y altura 182cm
Creando objecto de la clase Persona con nombre Jesus Alfonso y altura 168cm
Creando objecto de la clase Persona con nombre Ana y altura 162cm
Nombre: Juan, altura: 182
Nombre: Jesus Alfonso, altura: 168
Nombre: Ana, altura: 162

Se pueden bajar los archivos usados de ejemplo desde los siguientes enlaces:
Ejemplo 1: clase básica.
Ejemplo 2: clase con constructor.