Tutorial PHP orientado a objetos (POO) - Parte 2
apostol

Puntos: 139
Novato
 

14 - Sobreescritura del constructor.

Cuando creamos un objeto de una clase el primer método que se ejecuta es el constructor. Si la clase no tiene constructor pero la subclase si lo tiene, el que se ejecuta es el constructor de la clase padre:

Codigo:


Pruebas


class Operacion {
protected $valor1;
protected $valor2;
protected $resultado;
public function __construct($v1,$v2)
{
$this->valor1=$v1;
$this->valor2=$v2;
}
public function imprimirResultado()
{
echo $this->resultado.'
';
}
}

class Suma extends Operacion{
public function operar()
{
$this->resultado=$this->valor1+$this->valor2;
}
}

$suma=new Suma(10,10);
$suma->operar();
$suma->imprimirResultado();
?>



La clase Suma no tiene constructor pero cuando creamos un objeto de dicha clase le pasamos dos datos:

Codigo:

$suma=new Suma(10,10);

Esto es así ya que la clase padre si tiene constructor:

public function __construct($v1,$v2)
{
$this->valor1=$v1;
$this->valor2=$v2;
}



Ahora veremos un problema que la subclase también tenga constructor, es decir sobreescribimos el constructor de la clase padre.

Problema:Implementar la clase Operacion. El constructor recibe e inicializa los atributos $valor1 y $valor2. La subclase Suma añade un atributo $titulo. El constructor de la clase Suma recibe los dos valores a sumar y el título.

pagina1.php

Codigo:



Pruebas


class Operacion {
protected $valor1;
protected $valor2;
protected $resultado;
public function __construct($v1,$v2)
{
$this->valor1=$v1;
$this->valor2=$v2;
}
public function imprimirResultado()
{
echo $this->resultado.'
';
}
}

class Suma extends Operacion{
protected $titulo;
public function __construct($v1,$v2,$tit)
{
parent::__construct($v1,$v2);
$this->titulo=$tit;
}
public function operar()
{
echo $this->titulo;
echo $this->valor1.'+'.$this->valor2.' es ';
$this->resultado=$this->valor1+$this->valor2;
}
}

$suma=new Suma(10,10,'Suma de valores:');
$suma->operar();
$suma->imprimirResultado();
?>



Nuestra clase Operacion no a sufrido cambios. Veamos ahora la clase Suma que añade un atributo $titulo y constructor:

Codigo:

public function __construct($v1,$v2,$tit)
{
parent::__construct($v1,$v2);
$this->titulo=$tit;
}


El constructor de la clase Suma recibe tres parámetros. Lo primero que hacemos es llamar al constructor de la clase padre que tiene por objetivo inicializar los atributos $valor1 y $valor2 con la siguiente sintaxis:

Codigo:
parent::__construct($v1,$v2);


Mediante la palabra clave parent indicamos que llamamos el método __construct de la clase padre. Además utilizamos el operador ::.

El constructor de la clase Suma carga el atributo $titulo:

Codigo:
$this->titulo=$tit;


Ahora cuando creamos un objeto de la clase Suma debemos pasar los valores a los tres parámetros del constructor:

Codigo:
$suma=new Suma(10,10,'Suma de valores:');
$suma->operar();
$suma->imprimirResultado();


Si nos equivocamos y llamamos al constructor con dos parámetros:

Codigo:
$suma=new Suma(10,10);


Se muestra un warning:

Codigo:
Warning: Missing argument 3 for Suma::__construct()


15 - Clases abstractas y concretas.

Una clase abstracta tiene por objetivo agrupar atributos y métodos que luego serán heredados por otras subclases.

En conceptos anteriores planteamos las tres clase: Operacion, Suma y Resta. Vimos que no tenía sentido definir objetos de la clase Operacion (clase abstracta) y si definimos objetos de las clases Suma y Resta (clases concretas).

No es obligatorio que toda clase padre sea abstracta. Podemos tener por ejemplo un problema donde tengamos una clase padre (superclase) llamada Persona y una subclase llamada Empleado y luego necesitemos definir objetos tanto de la clase Persona como de la clase Empleado.

Existe una sintaxis en PHP para indicar que una clase es abstracta:

Codigo:
abstract class [nombre de clase] {
[atributos]
[metodos]
}


La ventaja de definir las clases abstractas con este modificador es que se producirá un error en tiempo de ejecución si queremos definir un objeto de dicha clase. Luego hay que tener bien en cuenta que solo podemos definir objetos de las clases concretas.

Luego el problema de herencia de las clases Operacion, Suma y Resta es:

Codigo:


Pruebas


abstract class Operacion {
protected $valor1;
protected $valor2;
protected $resultado;
public function cargar1($v)
{
$this->valor1=$v;
}
public function cargar2($v)
{
$this->valor2=$v;
}
public function imprimirResultado()
{
echo $this->resultado.'
';
}
}

class Suma extends Operacion{
public function operar()
{
$this->resultado=$this->valor1+$this->valor2;
}
}

class Resta extends Operacion{
public function operar()
{
$this->resultado=$this->valor1-$this->valor2;
}
}

$suma=new Suma();
$suma->cargar1(10);
$suma->cargar2(10);
$suma->operar();
echo 'El resultado de la suma de 10+10 es:';
$suma->imprimirResultado();

$resta=new Resta();
$resta->cargar1(10);
$resta->cargar2(5);
$resta->operar();
echo 'El resultado de la diferencia de 10-5 es:';
$resta->imprimirResultado();
?>



El único cambio que hemos producido a nuestro ejemplo está en la línea donde declaramos la clase Operacion:

abstract class Operacion {

No varía en nada la declaración de las otras dos clases:

Codigo:
class Suma extends Operacion{
...
}

class Resta extends Operacion{
...
}


Es decir que las clases concretas son aquellas que no le antecedemos el modificador abstract.

La definición de objetos de la clase Suma y Resta no varía:

Codigo:
$suma=new Suma();
$suma->cargar1(10);
$suma->cargar2(10);
$suma->operar();
echo 'El resultado de la suma de 10+10 es:';
$suma->imprimirResultado();

$resta=new Resta();
$resta->cargar1(10);
$resta->cargar2(5);
$resta->operar();
echo 'El resultado de la diferencia de 10-5 es:';
$resta->imprimirResultado();


Ahora bien si tratamos de definir un objeto de la clase Operación:

Codigo:
$operacion1=new Operacion();
$operacion1->cargar1(12);
$operacion1->cargar2(6);
$operacion1->imprimirResultado();


Se produce un error:

Fatal error: Cannot instantiate abstract class Operacion

Es decir que luego cuando utilicemos las clases que desarrollamos (definamos objetos) solo nos interesan las clases concretas.

16 - Métodos abstractos.

Vimos en conceptos anteriores que podemos definir clases abstractas que tienen por objetivo agrupar atributos y métodos comunes a un conjunto de subclases.

Si queremos que las subclases implementen comportamientos obligatoriamente podemos definir métodos abstractos.

Un método abstracto se declara en una clase pero no se lo implementa.

En nuestra clase Operacion tiene sentido declarar un método abstracto: operar. Esto hará que todas las clases que hereden de la clase Operación deban implementar el método operar.

Veamos la sintaxis para declarar un método abstracto con el problema de las clases Operacion, Suma y Resta.

Codigo:


Pruebas


abstract class Operacion {
protected $valor1;
protected $valor2;
protected $resultado;
public function cargar1($v)
{
$this->valor1=$v;
}
public function cargar2($v)
{
$this->valor2=$v;
}
public function imprimirResultado()
{
echo $this->resultado.'
';
}
public abstract function operar();
}

class Suma extends Operacion{
public function operar()
{
$this->resultado=$this->valor1+$this->valor2;
}
}

class Resta extends Operacion{
public function operar()
{
$this->resultado=$this->valor1-$this->valor2;
}
}

$suma=new Suma();
$suma->cargar1(10);
$suma->cargar2(10);
$suma->operar();
echo 'El resultado de la suma de 10+10 es:';
$suma->imprimirResultado();

$resta=new Resta();
$resta->cargar1(10);
$resta->cargar2(5);
$resta->operar();
echo 'El resultado de la diferencia de 10-5 es:';
$resta->imprimirResultado();
?>



Dentro de la clase Operacion declaramos un método pero no lo implementamos:

Codigo:
public abstract function operar();


Con esto logramos que todas las subclases de la clase Operacion deben implementar el método operar():

Codigo:
class Suma extends Operacion{
public function operar()
{
$this->resultado=$this->valor1+$this->valor2;
}
}


Si una subclase no lo implementa se produce un error (supongamos que la subclase Suma se nos olvida implementar el método operar):

Fatal error: Class Suma contains 1 abstract method and must therefore be declared abstract or
implement the remaining methods (Operacion::operar)

Solo se pueden declarar métodos abstractos dentro de una clase abstracta. Un método abstracto no puede ser definido con el modificador private (esto es obvio ya que no lo podría implementar la subclase)

17 - Métodos y clases final.

Si a un método le agregamos el modificador final significa que ninguna subclase puede sobreescribirlo. Este mismo modificador se lo puede aplicar a una clase, con esto estaríamos indicando que dicha clase no se puede heredar.

Confeccionaremos nuevamente las clases Operacion y Suma utilizando este modificador. Definiremos un método final en la clase Operacion y la subclase la definiremos de tipo final.

pagina1.php

Codigo:


Pruebas


class Operacion {
protected $valor1;
protected $valor2;
protected $resultado;
public function __construct($v1,$v2)
{
$this->valor1=$v1;
$this->valor2=$v2;
}
public final function imprimirResultado()
{
echo $this->resultado.'
';
}
}

final class Suma extends Operacion{
private $titulo;
public function __construct($v1,$v2,$tit)
{
Operacion::__construct($v1,$v2);
$this->titulo=$tit;
}
public function operar()
{
echo $this->titulo;
echo $this->valor1.'+'.$this->valor2.' es ';
$this->resultado=$this->valor1+$this->valor2;
}
}

$suma=new Suma(10,10,'Suma de valores:');
$suma->operar();
$suma->imprimirResultado();
?>



Podemos ver que la sintaxis para definir un método final es:

Codigo:
class Operacion {
...
public final function imprimirResultado()
{
echo $this->resultado.'
';
}
}


Luego si una subclase intenta redefinir dicho método:

Codigo:
class Suma extends Operacion {
...
public function imprimirResultado()
{
...
}
}


Se produce el siguiente error:

Fatal error: Cannot override final method Operacion::imprimirResultado()

El mismo concepto se aplica cuando queremos que una clase quede sellado y no dejar crear subclases a partir de ella:

Codigo:
final class Suma extends Operacion{
...
}


Agregando el modificador final previo a la declaración de la clase estamos indicando que dicha clase no se podrá heredar.

Luego si planteamos una clase que herede de la clase Suma:

Codigo:
class SumaTresValores extends Suma {
...
}


Produce el siguiente error:

Fatal error: Class SumaValores may not inherit from final class (Suma)

18 - Referencia y clonación de objetos.

Hay que tener en cuenta que un objeto es una estructura de datos compleja. Luego cuando asignamos una variable de tipo objeto a otra variable lo que estamos haciendo es guardar la referencia del objeto. No se está creando un nuevo objeto, sino otra variable por la que podemos acceder al mismo objeto.

Si queremos crear un nuevo objeto idéntico a otro debemos utilizar el operador clone.

El siguiente ejemplo muestra la diferencia entre asignación y clonación:

Codigo:


Pruebas


class Persona {
private $nombre;
private $edad;
public function fijarNombreEdad($nom,$ed)
{
$this->nombre=$nom;
$this->edad=$ed;
}
public function retornarNombre()
{
return $this->nombre;
}
public function retornarEdad()
{
return $this->edad;
}
}

$persona1=new Persona();
$persona1->fijarNombreEdad('Juan',20);
$x=$persona1;
echo 'Datos de la persona ($persona1):';
echo $persona1->retornarNombre().' - '.$persona1->retornarEdad().'
';
echo 'Datos de la persona ($x):';
echo $persona1->retornarNombre().' - '.$persona1->retornarEdad().'
';
$x->fijarNombreEdad('Ana',25);
echo 'Después de modificar los datos
';
echo 'Datos de la persona ($persona1):';
echo $persona1->retornarNombre().' - '.$persona1->retornarEdad().'
';
echo 'Datos de la persona ($x):';
echo $persona1->retornarNombre().' - '.$persona1->retornarEdad().'
';
$persona2=clone($persona1);
$persona1->fijarNombreEdad('Luis',50);
echo 'Después de modificar los datos de persona1
';
echo 'Datos de la persona ($persona1):';
echo $persona1->retornarNombre().' - '.$persona1->retornarEdad().'
';
echo 'Datos de la persona ($persona2):';
echo $persona2->retornarNombre().' - '.$persona2->retornarEdad().'
';
?>



Primero creamos un objeto de la clase Persona y cargamos su nombre y edad:

Codigo:
$persona1=new Persona();
$persona1->fijarNombreEdad('Juan',20);


Definimos una segunda variable $x y guardamos la referencia de la variable $persona1:

Codigo:
$x=$persona1;


Ahora imprimimos el nombre y edad de la persona accediéndolo primero por la variable $persona1 y luego por la variable $x (Se muestran los mismos datos porque en realidad estamos imprimiendo los atributos del mismo objeto):

Codigo:

echo 'Datos de la persona ($persona1):';
echo $persona1->retornarNombre().' - '.$persona1->retornarEdad().'
';
echo 'Datos de la persona ($x):';
echo $persona1->retornarNombre().' - '.$persona1->retornarEdad().'
';


Si modificamos el nombre y la edad de la persona llamando al método fijarNombreEdad mediante la variable $x:

Codigo:
$x->fijarNombreEdad('Ana',25);


luego imprimimos los atributos mediante las referencias al mismo objeto:

Codigo:
echo 'Después de modificar los datos
';
echo 'Datos de la persona ($persona1):';
echo $persona1->retornarNombre().' - '.$persona1->retornarEdad().'
';
echo 'Datos de la persona ($x):';
echo $persona1->retornarNombre().' - '.$persona1->retornarEdad().'
';


Para crear un segundo objeto y clonarlo del objeto referenciado por $persona1:

Codigo:
$persona2=clone($persona1);


Ahora cambiamos los atributos del primer objeto creado:

Codigo:
$persona1->fijarNombreEdad('Luis',50);


Luego imprimimos los atributos de los dos objetos:

Codigo:
echo 'Después de modificar los datos de persona1
';
echo 'Datos de la persona ($persona1):';
echo $persona1->retornarNombre().' - '.$persona1->retornarEdad().'
';
echo 'Datos de la persona ($persona2):';
echo $persona2->retornarNombre().' - '.$persona2->retornarEdad().'
';


Al ejecutarlo veremos que los datos que se imprimen son distintos.

Hay que diferenciar bien que el operador de asignación "=" no crea un nuevo objeto sino una nueva referencia a dicho objeto. Si queremos crear un nuevo objeto idéntico a uno ya existente debemos emplear el operador clone.

Cuando pasamos a un método un objeto lo que estamos pasando en realidad es la referencia a dicho objeto (no se crea un nuevo objeto)

19 - función __clone()

PHP nos permite crear un método que se llamará cuando ejecutemos el operador clone. Este método puede entre otras cosas inicializar algunos atributos.

Si no se define el método __clone se hará una copia idéntica del objeto que le pasamos como parámetro al operador clone.

Veamos un ejemplo: Crearemos una clase Persona que tenga como atributos su nombre y edad, definiremos los métodos para cargar y retornar los valores de sus atributos. Haremos que cuando clonemos un objeto de dicha clase la edad de la persona se fije con cero.

Codigo:


Pruebas


class Persona {
private $nombre;
private $edad;
public function fijarNombreEdad($nom,$ed)
{
$this->nombre=$nom;
$this->edad=$ed;
}
public function retornarNombre()
{
return $this->nombre;
}
public function retornarEdad()
{
return $this->edad;
}
public function __clone()
{
$this->edad=0;
}
}

$persona1=new Persona();
$persona1->fijarNombreEdad('Juan',20);
echo 'Datos de $persona1:';
echo $persona1->retornarNombre().' - '.$persona1->retornarEdad().'
';
$persona2=clone($persona1);
echo 'Datos de $persona2:';
echo $persona2->retornarNombre().' - '.$persona2->retornarEdad().'
';
?>



El método __clone se ejecutará cuando llamemos al operador clone para esta clase:

Codigo:
public function __clone()
{
$this->edad=0;
}


Es decir cuando realicemos la asignación:

Codigo:
$persona2=clone($persona1);


inicialmente se hace una copia idéntica de $persona1 pero luego se ejecuta el método __clone con lo que el atributo $edad se modifica.

Si queremos que una clase no pueda clonarse simplemente podemos implementar el siguiente código en el método __clone():

Codigo:
public function __clone()
{
die('No esta permitido clonar objetos de esta clase');
}


20 - Operador instanceof

Cuando tenemos una lista de objetos de distinto tipo y queremos saber si un objeto es de una determinada clase el lenguaje PHP nos provee del operador instanceof.

Confeccionaremos un problema que contenga un vector con objetos de la clase Empleado y Gerente. Luego calcularemos cuanto ganan en total los empleados y los gerentes.

Codigo:


Pruebas


abstract class Trabajador {
protected $nombre;
protected $sueldo;
public function __construct($nom,$sue)
{
$this->nombre=$nom;
$this->sueldo=$sue;
}
public function retornarSueldo()
{
return $this->sueldo;
}
}

class Empleado extends Trabajador {
}

class Gerente extends Trabajador {
}

$vec[]=new Empleado('juan',1200);
$vec[]=new Empleado('ana',1000);
$vec[]=new Empleado('carlos',1000);

$vec[]=new Gerente('jorge',25000);
$vec[]=new Gerente('marcos',8000);
$suma1=0;
$suma2=0;
for($f=0;$f {
if ($vec[$f] instanceof Empleado)
$suma1=$suma1+$vec[$f]->retornarSueldo();
else
if ($vec[$f] instanceof Gerente)
$suma2=$suma2+$vec[$f]->retornarSueldo();
}
echo 'Gastos en sueldos de Empleados:'.$suma1.'
';
echo 'Gastos en sueldos de Gerentes:'.$suma2.'
';

?>



Hemos planteado tres clases, la clase Trabajador es una clase abstracta:

Codigo:
abstract class Trabajador {
...
}


Las clases Empleado y Gerente son subclase de la clase Trabajador y en este caso por simplicidad no agregan ninguna funcionalidad a la clase padre:

Codigo:
class Empleado extends Trabajador {
}

class Gerente extends Trabajador {
}


Ahora veamos la parte que nos interesa, primero creamos 5 objetos, 3 de la clase Empleado y 2 de la clase Gerente:

Codigo:
$vec[]=new Empleado('juan',1200);
$vec[]=new Empleado('ana',1000);
$vec[]=new Empleado('carlos',1000);

$vec[]=new Gerente('jorge',25000);
$vec[]=new Gerente('marcos',8000);


Como podemos ver los 5 objetos se almacenan en un vector. Ahora tenemos que ver cuanto se gasta en sueldos pero separando lo que ganan los empleados y los gerentes:

Codigo:
$suma1=0;
$suma2=0;
for($f=0;$f {
if ($vec[$f] instanceof Empleado)
$suma1=$suma1+$vec[$f]->retornarSueldo();
else
if ($vec[$f] instanceof Gerente)
$suma2=$suma2+$vec[$f]->retornarSueldo();
}


Mediante el operador instanceof preguntamos por cada elemento del vector y verificamos si se trata de una instancia de la clase Empleado o de la clase Gerente.

Finalmente mostramos los acumuladores:

[/code]echo 'Gastos en sueldos de Empleados:'.$suma1.'
';
echo 'Gastos en sueldos de Gerentes:'.$suma2.'
';[/code]

21 - Método destructor de una clase (__destruct)

Otro método que se ejecuta automáticamente es el __destruct (destructor de la clase)

Las características de este método son:

El objetivo principal es liberar recursos que solicitó el objeto (conexión a la base de datos, creación de imágenes dinámicas etc.)
Es el último método que se ejecuta de la clase.
Se ejecuta en forma automática, es decir no tenemos que llamarlo.
Debe llamarse __destruct.
No retorna datos.
Es menos común su uso que el constructor, ya que PHP gestiona bastante bien la liberación de recursos en forma automática.

Para ver su sintaxis e implementación confeccionaremos el siguiente problema: Implementar una clase Banner que muestre un texto generando un gráfico en forma dinámica. Liberar los recursos en el destructor. En el constructor recibir el texto publicitario.

Codigo:
class Banner {
private $ancho;
private $alto;
private $mensaje;
private $imagen;
private $colorTexto;
private $colorFondo;
public function __construct($an,$al,$men)
{
$this->ancho=$an;
$this->alto=$al;
$this->mensaje=$men;
$this->imagen=imageCreate($this->ancho,$this->alto);
$this->colorTexto=imageColorAllocate($this->imagen,255,255,0);
$this->colorFondo=imageColorAllocate($this->imagen,255,0,0);
imageFill($this->imagen,0,0,$this->colorFondo);
}
public function graficar()
{
imageString ($this->imagen,5,50,10, $this->mensaje,$this->colorFuente);
header ("Content-type: image/png");
imagePNG ($this->imagen);
}
public function __destruct()
{
imageDestroy($this->imagen);
}
}

$baner1=new Banner(428,45,'Sistema de Ventas por Mayor y Menor');
$baner1->graficar();
?>


Se trata de un archivo PHP puro ya que se genera una imagen PNG y no un archivo HTML.

Al constructor llega el texto a imprimir y el ancho y alto de la imagen. En el constructor creamos el manejador para la imagen y creamos dos colores para la fuente y el fondo del banner.

Codigo:
public function __construct($an,$al,$men)
{
$this->ancho=$an;
$this->alto=$al;
$this->mensaje=$men;
$this->imagen=imageCreate($this->ancho,$this->alto);
$this->colorTexto=imageColorAllocate($this->imagen,255,255,0);
$this->colorFondo=imageColorAllocate($this->imagen,255,0,0);
imageFill($this->imagen,0,0,$this->colorFondo);
}


El método graficar genera la imagen dinámica propiamente dicha:

Codigo:
public function graficar()
{
imageString ($this->imagen,5,50,10, $this->mensaje,$this->colorFuente);
header ("Content-type: image/png");
imagePNG ($this->imagen);
}


Y por último tenemos el destructor que libera el manejador de la imagen:

Codigo:
public function __destruct()
{
imageDestroy($this->imagen);
}


Cuando creamos un objeto de la clase Banner en ningún momento llamamos al destructor (se llama automáticamente previo a la liberación del objeto:

Codigo:
$baner1=new Banner(428,45,'Sistema de Ventas por Mayor y Menor');
$baner1->graficar();


22 - Métodos estáticos de una clase (static)

Un método estático pertenece a la clase pero no puede acceder a los atributos de una instancia. La característica fundamental es que un método estático se puede llamar sin tener que crear un objeto de dicha clase.

Un método estático es lo más parecido a una función de un lenguaje estructurado. Solo que se lo encapsula dentro de una clase.

Confeccionaremos una clase Cadena que tenga una conjunto de métodos estáticos:

Codigo:


Pruebas


class Cadena {
public static function largo($cad)
{
return strlen($cad);
}
public static function mayusculas($cad)
{
return strtoupper($cad);
}
public static function minusculas($cad)
{
return strtolower($cad);
}
}

$c='Hola';
echo 'Cadena original:'.$c;
echo '
';
echo 'Largo:'.Cadena::largo($c);
echo '
';
echo 'Toda en mayúsculas:'.Cadena::mayusculas($c);
echo '
';
echo 'Toda en minúsculas:'.Cadena::minusculas($c);
?>



Para definir un método estático utilizamos la palabra clave static luego del modificador de acceso al método:

Codigo:
public static function largo($cad)
{
return strlen($cad);
}


Hay que tener en cuenta que un método estático no puede acceder a los atributos de la clase, ya que un método estático normalmente se lo llama sin crear un objeto de dicha clase:

Codigo:
echo 'Largo:'.Cadena::largo($c);


La sintaxis para llamar un método estático como vemos es distinta a la llamada de métodos de un objeto. Indicamos primero el nombre de la clase, luego el operador '::' y por último indicamos en nombre del método estático a llamar.

Puntos: Sin Puntos - Opsss: Para dar puntos debes estar conectado... - Post Publicado hace 12 años
Facebook Twitter Sonico Delicious Digg My Space Technorati Google     

Solo Miembros Activos pueden comentar.





Subir
Si no tenés 1360x768 jodete.
Hecho por Tomás - Inspirado en Taringa
Lunes 29 de Abril del 2024
RSS Feed - Sitemap
Powered by PHP - Apache - MySQL