viernes, 14 de noviembre de 2014

[Parte 2] Programacion orientada a objetos con PHP

Teorías avanzadas

La herencia de objetos es cuando una clase se deriva de otra, al igual que los seres humanos heredan cualidades de sus padres. Por supuesto las "cualidades" en el mundo orientado a objetos son los atributos (variables) y métodos (funciones). A traves de la herencia, se puede definir una clase que se "nace" de los mismos atributos y metodos como la otra. La clase hija heredada puede incluso tener sus propias "cualidades" unicas que el padre no tiene.


La clase hijo hereda (es decir, tiene) todos los atributos y metodos de su clase padre.


Las clases hijo pueden tener sus propios miembros además de los que heredaron. De esta manera un hijo puede separarse (funcionalmente hablando) de su padre.

Indicando herencia en UML

En un diagrama de clases UML, la herencia es representada mediante una flecha entre las dos clases, como en las figuras anteriores. La flecha debe de ir de la subclase a la clase base (es decir, los puntos de flecha a los padres). 
Convencionalmente, la clase padre se coloca por encima de las clases hijas.

Terminologia de herencia

Con las definiciones de clase, los terminos principales son atributos y metodos, variables y funciones, respectivamente. La combinacion de atributos y metodos componen los miembros de una clase. Con la herencia tiene una clase padre y una clase hijo: esta ultima se hereda de la antigua. Tambien vera que estos son descritos como clase base o superclase y su clase derivada o subclase.

Pero la herencia no es necesariamente una relacion uno-a-uno. No hay limite para el numero de veces que la herencia puede ocurrir: multiples clases pueden ser heredades de la clase padre. O una clase puede ser hija de una clase hija. Esto hablando de la poderosa reutilizacion de codigo de la clase.



Una sola clase padre puede tener descendencia sin limite, cada una personalizada en su camino.


La herencia puede teoricamente tener profundidad ilimitada.

Una vez que se haya definido una clase que se hereda de otra, no se necesita mucho tiempo para comenzar pensando en lo agradable que seria si se comporta un poco de manera diferente. Puede añadir nuevos atributos y metodos, ¿Que pasa si usted quiere cambiar el comportamiento de los metodos de la clase padre? Seria un error al cambiar la definicion de la clase padre (presumiblemente funciona como deberia, y ademas otras clases se podrian heredar de la misma). En su lugar puede sobreescribir un metodo de la clase padre para personalizarlo para una nueva clase. Este es el polimorfismo, donde al llamar al mismo metodo puede tener resultados diferentes, dependiendo del tipo de objeto. 

La herencia de las clases

Una manera en que los objetos hacen una programacion mas rapida es la capacidad de utilizar una definicion de clase como base para otra. Este proceso se conoce como herencia.

Siguiendo con el ejemplo de la parte anterior donde teniamos una clase usuario que tenia de atributos id de usuario, correo electronico, contraseña y tiene los metodos de inicio de sesion y cierre de sesion, se podria crear otra clase llamada Administrador que es una extension de usuario. Junto con las variables y funciones mencionadas, un objeto Administrador podria tener tambien un atributo NiveldeAcceso y el metodo editarUsuario.

Este tipo de clases significa que las dos tienen una relacion, en la que Administrador es un tipo de usuario. Cuando estas en estas situaciones de diseño donde una cosa es simplemente un tipo mas especifico de otra cosa, usted probablemente querra usar herencia. 

Para crear una clase hija desde uno de los padres, hacemos uso de la palabra clave extends. Asumiendo que ya tiene definido el NombredeLaClase, usted puede crear una clase hija de este modo:

class ClaseHijo extends NombredeLaClase{}
Como esta escrito, la clase sera ClaseHijo y contendra todos los miembros de su padre, NombredeLaCLase. Ahora se puede modificar esta clase para adaptarla a sus necesidades especificas sin la alteracion de la clase original. Idealmente, una vez que ha creado una clase padre solida, que nunca tendra que modificar de nuevo, puede usar las clases hijos para adaptar el codigo a sus requisitos individuales.

La clase Administrador puede tener todos los mismos miembros que Usuario, al mismo tiempo que añade las suyas propias.

La palabra clave instanceof

La palabra clave instanceof puede ser usada para ver si es un objeto en particular de un cierto tipo de clase:

if($obj instanceof AlgunaClase){ ... 

El script de PHP debe tener acceso a la definicion AlgunaClase 

Para una implementacion de este ejemplo, vamos a comenzar con un ejemplo de mascotas (comprensible) algo tonto. Digamos que usted tiene 2 mascotas: un gato y un perro. Ambos animales tienen un nombre, y ambos comen y duermen. Los gatos difieren de los perros, ya que pueden trepar a los arboles y los perros difieren de los gatos en que pueden recuperar bolas. Ser capaz de describir estas cualidades y relaciones en un lenguaje sencillo conduce a la estructura de la herencia que debe crear:  




Un ejemplo de codigo seria el siguiente:


 <!DOCTYPE html>
<html lang="es">
<head>
 <meta charset="utf-8">
 <title>Mascotas</title>
</head>
<body>
<?php
 # Mascotas
 // Esta pagina define y usa clas clases mascota, gato y perro.

 # *** Clases *** #
 /*
 * Clase Mascota
 * Esta clase contiene un atributo:
 * nombre
 * Esta clase contiene tres metodos:
 * __construct()
 * comer()
 * dormir()
 */
 class Mascota{
  // Declaramos los atributos:
  public $nombre;

  // Constructor que asigna nombre a la mascota:
  function __construct($nombre_mascota){
   $this->nombre = $nombre_mascota;
  }

  // La mascota come:
  function comer(){
   echo "<p>$this->nombre esta comiendo.</p>";
  }

  // La mascota puede domir:
  function dormir(){
   echo "<p>$this->nombre esta durmiendo</p>";
  }
 } // Fin de la clase mascota

 /* La clase Perro extiende la clase Mascota
 * Perro tiene un metodo adicional: recuperar()
 */
 class Perro extends Mascota{
  function recuperar(){
   echo "<p>$this->nombre recupera la bola.</p>";
  }
 } // Fin de la clase Perro

 /* La clase Gato extiende la clase Mascota
 * Gato agrega un metodo adicional: subir()
 */
 class Gato extends Mascota{
  function subir(){
   echo "<p>$this->nombre esta subiendo</p>";
  }
 } // Fin de la clase Gato

 # *** Fin de las clases *** #


 // Creamos un Perro:
 $Perro = new Perro('Satchel');
 // Creamos un Gato:
 $Gato = new Gato('Bucky');

 // Darles de comer:
 $Perro->comer();
 $Gato->comer();

 // Hora de la siesta:
 $Perro->dormir();
 $Gato->dormir();

 // Hacer cosa especifica de los animales:
 $Perro->recuperar();
 $Gato->subir();

 // Eliminamos los objetos:
 unset($Perro,$Gato);

?>
</body>
</html>


El codigo es muy basico y se entiende perfectamente ya que todo lo hemos visto anteriormente, lo unico que hay que explicar aqui es que extendemos la clase Mascota para tener dos clases personalizadas Perro y Gato las cuales tienen unos metodos diferentes ya que un Gato y un Perro realizan diferentes acciones.





Heredando Constructores y Destructores

El ejemplo Mascota muestra como podemos crear una clase (Mascota) y que de la misma se deriven otras clases (Perro, Gato). Estas clases pueden tener sus propios metodos, unicos a ellos mismos, tal como subir() y recuperar().


Hay dos metodos que son muy comunes en muchas clases: Los constructores y los destructores (los hemos visto anteriormente). La clase Mascota tiene un constructor, pero no hay necesidad de un destructor. ¿Que pasaria si el Gato o el Perro tuvieran un constructor? Por definicion este metodo se llama siempre __construct(). ¿Como determinamos que version del constructor de PHP ejecutamos?




Cuando se crea un objeto, PHP siempre llamara al constructor de ese objeto.

Como regla general, PHP siempre llamara al constructor para la clase. La misma regla se aplica para los destructores. Ademas, a diferencia de otros lenguajes de POO, en PHP, cuando se crea un objeto de una clase hijo, el constructor de la clase padre no se llamara automaticamente. 

Vamos a extender la clase Rectangulo que hicimos anteriormente, para crear una clase Cuadrado.




 <!DOCTYPE html>
<html lang="es">
<head>
 <meta charset="utf-8">
 <title>Cuadrado</title>
</head>
<body>
<?php
 // Esta pagina declara y usa la clase Cuadrado que es derivada de la clase Rectangulo
 
 // incluimos la primera definicion de la clase:
 require('Rectangulo.php');
 class Cuadrado extends Rectangulo{
  // Creamos la clase Cuadrado
  // Este valor es asignado para el rectangulo con los atributos ancho y alto
  function __construct($lado = 0){
   $this->ancho = $lado;
   $this->alto = $lado;
  } 

 }// Fin de la clase Cuadrado.

 // Dimensiones del Rectangulo:
 $ancho = 21;
 $alto = 98;

 // Imprimimos una pequeña introduccion:
 echo "<h2>Con un ancho de $ancho y un alto de $alto ...</h2>";

 // Creamos un nuevo Rectangulo:
 $r = new Rectangulo($ancho,$alto);

 // Imprimimos el area:
 echo '<p>El area del rectangulo es:'.$r->obtenerArea().'</p>';

 // Imprimimos el perimetro
 echo '<p>El perimetro del rectangulo es:'.$r->obtenerPerimetro().'</p>';

 // Dimensiones del cuadrado:
 $lado = 60;

 // Imprimimos una pequeña introduccion:
 echo "<h2>Con cada lado $lado ...</h2>";

 // Creamos un nuevo objeto:
 $c = new Cuadrado($lado);

 // Imprimimos el area:
 echo '<p>El area del cuadrado es:'.$c->obtenerArea().'</p>';

 // Imprimimos el perimetro
 echo '<p>El perimetro del cuadrado es:'.$c->obtenerPerimetro().'</p>';

 // Eliminamos los objetos:
 unset($r,$c); 
?>
</body>
</html>







Incluso si el constructor de la clase Cuadrado toma un solo argumento, el uso de los metodos del Rectangulo, y el resultado final, funcionan de la misma manera. 

Diseño de herencia simple

Cada vez que una clase hereda otra, el resultado debe ser una descripcion mas especifica de una cosa. Por lo tanto, si nos vamos a Mascota para Perro o Gato y del Rectangulo para Cuadrado. Al decidir donde colocar los metodos, incluyendo constructores y destructores, usted tiene que pensar si esa funcionalidad es universal o mas especifico.

En el ejemplo Mascota, el constructor establece el nombre de la mascota, que es universal para todas las mascotas. Asi que Perro y Gato no necesitan sus propios constructores. En el ejemplo Rectangulo, su constructor establece el alto y el ancho. Pero un cuadrado no tiene dos dimensiones distintas, por ello debemos tener un nuevo constructor para que sea valido.

Notas:

Los constructores nunca devuelven nada.

Una recomendacion de POO es que todas las clases tengan un constructor heredado o de otra manera.

Usted puede llamar al constructor de la clase padre, si es necesario.

Sobreescritura de metodos

Para sobreescribir un metodo en PHP, la subclase debe definir un metodo con el mismo nombre exacto y el numero de argumentos que la clase padre.

 <?php
 class AlgunaClase{
  function gritar($count = 1){
   for($i = 0;$i < $count;$i++){
    echo 'Eek!<br />';
   }
  }
 }

 class AlgunaOtraClase extends AlgunaClase{
  function gritar($count = 1){
   for($i=0;$i<$count;$i++){
    echo 'Whoohoo!<br />';
   }
  }
 }

 $obj1 = new AlgunaClase();
 $obj1->gritar();
 $obj1->gritar(2);
 $obj2 = new AlgunaOtraClase();
 $obj2->gritar();
 $obj2->gritar(2);
?>


La sobreescritura de metodos es una caracteristica comun y util de la programacion orientada a objetos. Como se menciono anteriormente, los metodos sobreescritos crean polimorfismo, donde al llamar al mismo metodo puede obtener resultados diferentes, dependiendo del tipo de objeto. 

Como un simple ejemplo de esto, vamos a volver a las clases Mascota, Perro y Gato, en lugar de tener los metodos subir() y recuperar(), implementaremos una sobreescritura de metodo llamada jugar().



 <!DOCTYPE html>
<html lang="es">
<head>
 <meta charset="utf-8">
 <title>Mascotas</title>
</head>
<body>
<?php
 // Esta pagina define el uso de la clase Mascota, Perro y Gato.

 # *** CLASES *** #
 
 /* Clase Mascota
 * La clase contiene un atributo
 * La clase contiene 4 metodos:
 * - __construct()
 * - comer()
 * - dormir()
 * - jugar()
 */
 class Mascota{
  public $nombre;

  function __construct($nombre){
   $this->nombre = $nombre;
  }

  function comer(){
   echo "<p>$this->nombre esta comiendo.</p>";
  }

  function dormir(){
   echo "<p>$this->nombre esta durmiendo.</p>";
  }

  // Mascota puede jugar:
  function jugar(){
   echo "<p>$this->nombre esta jugando.</p>";
  }
 } // Fin de la clase Mascota

 /* La clase Gato se extiende desde la clase Mascota
 * Gato sobreescribe jugar()
 */

 class Gato extends Mascota{
  function jugar(){
   echo "<p>$this->nombre esta subiendo.</p>";
  }
 } // Fin de la clase Gato.

 /* Perro extiende la clase Mascota 
 * Perro sobreescribe jugar()
 */

 class Perro extends Mascota{
  function jugar(){
   echo "<p>$this->nombre esta recuperando.</p>";
  }
 } // Fin de la clase Perro

 # *** Fin de las CLASES *** #

 // Creamos un nuevo Perro
 $Perro = new Perro('Satchel');

 // Creamos un nuevo Gato
 $Gato = new Gato('Bucky');

 // Creamos un tipo desconocido de Mascota
 $Mascota = new Mascota('Rob');

 // Les damos de comer
 $Perro->comer();
 $Gato->comer();
 $Mascota->comer();

 // Hora de la siesta:
 $Perro->dormir();
 $Gato->dormir();
 $Mascota->dormir();

 // Pedirles que jueguen
 $Perro->jugar();
 $Gato->jugar();
 $Mascota->jugar();

 // Eliminamos los objetos:
 unset($Perro, $Gato, $Mascota);
?>
</body>
</html>



Metodos finales

La mayoria de los metodos de las clases se pueden sobrescribir. La excepcion es que si una funcion es definida como definitiva:

final function mifuncion(){...}

La definicion de un metodo final no puede ser alterado por cualquier subclase. Una clase tambien puede ser declarada como definitiva lo que significa que no puede ser extendida.

La clase Cuadrado puede ser logicamente sobreescribir el metodo de esCuadrado() de la clase Rectangulo. Simplemente se define:

function esCuadrado(){ return true; }

Control de Acceso

El control de Acceso, tambien se llama la visibilidad, dicta como son accesibles los atributos y metodos... en otras palabras: donde pueden ser referenciados los miembros de la clase y en el que son inaccesibles.

Hay tres niveles de visibilidad: public, protected y private. Para establecer la visibilidad de un atributo, la declaracion de la variable lleva como prefijo una de estas palabras clave:

class NombreClase{
        public $var1 = 'Hola';
        private $var2 = 'Mundo';
        protected $var3 = 34;
    }
Ya has estado haciendo esto, por lo que indica la visibilidad de un atributo que se requiere en PHP. Hasta el momento todos los atributos han sido declarados como publicos (public).

Para establecer la visibilidad de un metodo, el prefijo de la declaracion de la funcion debe estar con una de las palabras claves:

class NombreClase{
        public function miFuncion(){
            // codigo
        }
    }
Los metodos que carecen de la declaracion de accesibilidad son considerados como publicos. Y debido a que la mayoria de los metodos son publicos, la visibilidad para ellos se omite con frecuencia.

Piense en cada termino como la prescripcion de un circulo mas limitado en el cada miembro se puede acceder.


Cuanto mas restringida la visibilidad, es mas pequeño el reino donde el atributo y metodo son accesibles.

Un miembro publico es el mas accesible: esta disponible para metodos dentro de la propia clase, en clases heredadas, y fuera de la clase. Por ejemplo, debido a que el atributo $nombre en Mascota es publico, usted puede hacer esto:

$Mascota = new Mascota('Charlie');
 $Mascota->nombre = 'Fungo';
Una vez mas,$nombre es publico por lo cual tambien esta disponible en las clases Perro y Gato, como hemos visto.

Los miembros protegidos solo se pueden acceder dentro de la clase y subclases derivadas. Esto significa que si el atributo $nombre en Mascota se hace protected, usted todavia podria utilizarlo dentro de los metodos que se encuentran en las clases de Perro o Gato, pero no directamente a traves de una instancia de objeto de cualquiera de esas clases.

Privado es mas restrictivo: aquellos miembros solo son accesibles dentro de la clase que los define. Miebros de la clase privada no se pueden acceder por la subclase o por medio de una instancia de objeto de esa clase. Si el atributo $nombre en Mascota fue hecho privado, solo se podria hacer referencia a el dentro de la definicion de la clase de Mascota (es decir, dentro de uno de sus metodos).

Puede parecer extraño para una clase que tiene atributos, o los miembros que son inaccesibles, pero este es un valioso importante concepto de POO, llamado encapsulacion. En pocas palabras la encapsulacion, es la ocultacion de informacion (datos o procesos reales) que no necesita estar disponible fuera de la clase. Desde una perspectiva de diseño, una buena clase es una entidad utilizable sin saber necesariamente como funciona internamente. Por ejemplo, una base de datos necesita una conexion a la base de datos interna, pero no hay razon para que la conexion pueda ser accesible fuera de la clase (de hecho, no deberia).

En cuanto a la clase de Mascota, su atributo $nombre debe hacerse de modo protegido. Tiene sentido que la clase y sus subclases para acceder al nombre, pero usted no deberia ser capaz de cambiar el nombre fuera de la clase. Lo mismo se aplica a los atributos $ancho y $alto de Rectangulo que deben ser protegidos. Al igual que la mayoria de los conceptos de programacion orientada a objetos, hay dos cosas que usted debe de aprender: como funciona la visibilidad y como se utiliza. Para tener claro como funciona el control de acceso, vamos a usar un ejemplo ficticio donde solo juega un papel la accesibilidad de los atributos. Despues de esto el tema de visibilidad sera mas claro:



  

 <!DOCTYPE html>
<html lang="es">
<head>
 <meta charset="utf-8">
 <title>Visibilidad</title>
</head>
<body>
<?php
 // Esta pagina define y usa las clases Prueba y PequenaPrueba.

 # *** CLASES *** #
 /* Clase Prueba
 * Esta clase contiene tres atributos:
 * - public $public
 * - protected $protected
 * - private $_private
 * La clase define un metodo: imprimirVariable().
 */

 class Prueba{
  // Declaramos los atributos:
  public $public = 'publica';
  protected $protected = 'protegida';
  private $_private = 'privada';

  // Funcion para imprimir el valor de la variable:
  function imprimirVariable($var){
   echo "<p>En prueba, \$$var: '{$this->$var}'.</p>";
  }
 } // Fin de la clase Prueba

 /* PruebaPequena extiende la clase Prueba
 * PruebaPequena sobreescribe imprimirVariable().
 */
 class PruebaPequena extends Prueba{
  function imprimirVariable($var){
   echo "<p>En PruebaPequena, \$$var: '{$this->$var}'.</p>";
  }
 } // Fin de la clase PruebaPequena

 # *** Fin de las clases *** #

 // Creamos los objetos:
 $padre = new Prueba();
 $hijo = new PruebaPequena();

 // Imprimimos el valor actual de $public
 echo '<h1>Public</h1>';
 echo '<h2>Inicializando...</h2>';
 $padre->imprimirVariable('public');
 $hijo->imprimirVariable('public');

 // Modificamos $public y lo reimprimimos:
 echo '<h2>Modificando $padre->public</h2>';
 $padre->public = 'modificado';
 $padre->imprimirVariable('public');
 $hijo->imprimirVariable('public');

 // Imprimimos el valor actual de $protected
 echo '<h1>Protected</h1>';
 echo '<h2>Inicializando...</h2>';
 $padre->imprimirVariable('protected');
 $hijo->imprimirVariable('protected');

 // Intento de modificar $protected y lo reimprimimos:
 echo '<h2>Modificando $padre->protected</h2>';
 $padre->protected = 'modificado';
 $padre->imprimirVariable('protected');
 $hijo->imprimirVariable('protected');

 // Imprimimos el valor actual de $_private
 echo '<hr><h1>Private</h1>';
 echo '<h2>Inicializando...</h2>';
 $padre->imprimirVariable('_private');
 $hijo->imprimirVariable('_private');

 // Intento de modificar $_private y lo reimprimimos:
 echo '<h2>Modificando $padre->_private</h2>';
 $padre->_private = 'modificado';
 $padre->imprimirVariable('_private');
 $hijo->imprimirVariable('_private');

 // Eliminamos los objetos:
 unset($padre,$hijo);
?>
</body>
</html>

- Los atributos publicos pueden ser accedidos por cualquiera y ser modificados:


- Intentar modificar un atributo protegido usando la sintaxis: $obj->var resultara en error fatal 


- El intento de hacer referencia a $this->_private dentro de la clase PruebaPequena - es lo que sucede cuando usted llama $hijo->imprimirVariable('_private') - crea una aviso, como la clase no tiene dicho atributo (por que ni lo ha heredado, ni definido), intentando referirse a $padre->_private resulta en un error faltal.




Indicando la visibilidad en UML

En un diagrama de clases UML, la accesibilidad de un miembro de la clase puede ser indicada como utilizando como prefijo lo siguiente para cada miembro de la clase:

* +, para public
* -, para private
* #, para protected

Por lo tanto la clase Mascota puede ser definida asi:

+nombre:string
+__construct($nombre:string):void


Muchos programadores podrian argumentar que todos los atributos deben ser protected o private, entonces ellos nunca accederian directamente desde fuera de la clase. Tu podrias entonces escribir metodos "get" y "set" para acceder a ellos cuando los necesites.

Un metodo diseñado para devolver un atributo es llamado getter o accessor. Un metodo diseñado para asignar valores a un atributo es llamado setter o mutator.

Utilizando el operador de resolucion de alcance

POO tiene algunos de sus propios operadores, como ya has visto con ->, utilizado por los objetos para acceder a sus miembros. Otro es el operador de resolucion de alcance: la combinacion de dos puntos juntos (::). Es usado para acceder a los miebros a traves de clases, no objetos.

    NombreClase::nombreMetodo();
    NombreClase::nombrePropiedad();


Hay dos lugares en los que se utiliza este constructor:

* Dentro de las clases, para evitar la confunsion cuando las clases heredadas tienen mismos atributos y metodos.
* Fuera de clases, para tener acceso a los miembros sin crear primero los objetos.

Fuera de una clase, tendra que especificar el nombre de la clase, como en el codigo anterior. Dentro de una clase, sin embargo, son palabras claves especiales las que usted debe utilizar. Mientras que usted puede usar $this dentro de una clase para hacer referencia a una instancia dentro del objeto actual, la palabra clave self es una referencia a la clase actual. 

class AlgunaClase{
        function __construct(){
            self::hacerEsto();
        }
        protected function hacerEsto(){
            echo '¡Hecho!';
        }
    }


En este codigo, self::hacerEsto() invocara el metodo hacerEsto() de la clase actual.

(Como nota al margen de compresion de visibilidad, la funcion hacerEsto() se define en AlgunaClase solo puede ser llamado dentro de AlgunaClase o por metodos dentro de clases heredadas, por que hacerEsto() se define como protegida)

Para hacer referencia a un miembro de una clase padre, utilice el operador de resolucion de alcance con la palabra clave parent:

class AlgunaOtraClase extends AlgunaClase{
        function __construct(){
            parent::hacerEsto();
        }
    }


En su mayor parte, va utilizar el operador de resolucion de alcance, para tener acceso a metodos sobreescritos. Tambien lo utilizan con miembros de clase estaticos y constantes, mientras tanto veremos como una demostracion utilizando de nuevo nuestras clases Mascota, Perro y Gato.



 <!DOCTYPE html>
<html lang="es">
<head>
 <meta charset="utf-8">
 <title>Mascotas</title>
</head>
<body>
<?php
 // Esta pagina define el uso de las clases Mascota, Perro y Gato.
 
 # *** CLASES *** #

 /* Clase Mascota
 * Esta clase contiene un atributo: nombre
 * Esta clase contiene 4 metodos:
 * - __construct()
 * - comer()
 * - dormir()
 * - jugar()
 */
 class Mascota{
  public $nombre;
  function __construct($nombre){
   $this->nombre = $nombre;
   self::dormir();
  }
  function comer(){
   echo "<p>$this->nombre esta comiendo.</p>";
  }
  function dormir(){
   echo "<p>$this->nombre esta durmiendo.</p>";
  }
  function jugar(){
   echo "<p>$this->nombre esta jugando.</p>";
  }
 } // Fin de la clase Mascota
 /* Clase Gato extiende Mascota.
 * Gato sobreescribe jugar().
 */
 class Gato extends Mascota{
  function jugar(){
   // Llamamos el metodo Mascota::jugar():
   parent::jugar();
   echo "<p>$this->nombre esta subiendo.</p>";
  }
 } // Fin de la clase Gato.
 /* Clase Perro extiende Mascota.
 * Perro sobreescribe jugar().
 */
 class Perro extends Mascota{
  function jugar(){
   // Llamamos al metodo Mascota::jugar():
   parent::jugar();
   echo "<p>$this->nombre esta devolviendo la pelota.</p>";
  }
 } // Fin de la clase Perro.

 # *** Fin de las CLASES *** #

 // Creamos un Perro:
 $Perro = new Perro('Satchel');

 // Creamos un Gato:
 $Gato = new Gato('Bucky');

 // Creamos un tipo desconocido de Mascota:
 $Mascota = new Mascota('Rob');

 // Les damos de comer:
 $Perro->comer();
 $Gato->comer();
 $Mascota->comer();

 // Hora de la siesta:
 $Perro->dormir();
 $Gato->dormir();
 $Mascota->dormir();

 // Los llevamos a jugar:
 $Perro->jugar();
 $Gato->jugar();
 $Mascota->jugar();

 // Eliminamos los objetos:
 unset($Perro,$Gato,$Mascota);
?>
</body>
</html>


El codigo modificado llama ahora el metodo jugar() de Mascota, cada vez que un Gato o un Perro juega.

En Perro y Gato se puede tambien utilizar Mascota::jugar(). Pero mediante el uso de parent::jugar(), se minimiza el riesgo de problemas en el futuro si se cambiesen las definiciones de las clases.

Creacion de miembros estaticos

Una variable estatica recuerda su valor cada vez que se le llama desde una funcion.

 <?php
 function prueba(){
  static $n = 1;
  echo "$n<br>";
  $n++;
 }
 prueba();
 prueba();
 prueba();
?>


Una variable estatica conserva su valor a lo largo de varias llamadas (en el mismo script).

Cada llamada de la funcion prueba() incrementa el valor de n por 1. Si $n no fuese declarado como estatico, cada llamada de la funcion imprimiria el numero 1.

Con atributos de clase estaticos, el concepto es el mismo, salvo que una variable estatica es recordada en todas las instancias de esa clase (en todos los objetos en funcion de la clase). Para declarar un atributo estatico, utilice la palabra clave static despues de el indicador de visibilidad:

class AlgunaClase{
        public static $var = 'valor';
    }



Las variables estaticas se diferencian de los atributos estandar en que no se puede acceder a ellos dentro de la clase utilizando $this. En su lugar debe utilizar self, seguido por el operador de resolucion de alcance (::), seguido del nombre de la variable con su signo inicial en dolar:

class AlgunaClase{
        public static $contador = 0;
        function __construct(){
            self::$contador++;
        }
    }


El codigo anterior crea un contador. Cada vez que un objeto nuevo se crea:

$obj = new AlgunaClase();

El $contador sube 1. Los metodos estaticos se crean de la misma manera:

class AlgunaClase{
        public static $contador = 0;
        public static function hacerEsto(){
            // Codigo
        }
    }
    echo AlgunaClase::$contador; // 0
    AlgunaClase::hacerEsto();



(De hecho, no solo se puede acceder a los miembros estaticos sin crear un objeto, las propiedades estaticas no pueden ser accedidas a traves de un objeto, aunque los metodos estaticos pueden ser).
Para jugar a esto, vamos a crear una nueva clase de Mascotas que utiliza un atributo estatico y un metodo estatico. El atributo se utilizara para contar el numero de animales en existencia. El metodo estatico devolvera el numero de animales. Como esta es una demostracion de este nuevo concepto, no se crearan otros metodos:

 <!DOCTYPE html>
<html>
<head>
 <meta charset="utf-8">
 <title>Estatico</title>
</head>
<body>
<?php
 // Esta pagina define el uso de las clases Mascota, Gato y Perro.
 # *** CLASES *** #
 /* Clase Mascota.
 * Esta clase toneien dos atributos:
 * - protected nombre
 * - private static _count
 * Esta clase contiene 3 metodos:
 * - __construct()
 * - __destruct()
 * - public static obtenerContador()
 */
 class Mascota{
  // Declaramos los atributos:
  protected $nombre;
  private static $_count = 0;

  // El constructor asigna el nombre de la Mascota
  // y incrementa el contador
  function __construct($nombre){
   $this->nombre = $nombre;

   // Incrementamos el contador:
   self::$_count++;
  }

  // El destructor decrementa el contador:
  function __destruct(){
   self::$_count--;
  }

  // Creamos un metodo estatico para retornar el contador:
  public static function obtenerContador(){
   return self::$_count;
  }
 } // Fin de la clase Mascota.

 /* La clase Gato extiende la clase Mascota */
 class Gato extends Mascota{

 } // Fin de la clase Gato.
 /* La clase Perro extiende la clase Mascota */
 class Perro extends Mascota{

 } // Fin de la clase Perro.

 /* La clase Huron extiende la clase Mascota */
 class Huron extends Mascota{

 }  // Fin de la clase Huron.

 /* La clase Mono extiende la clase Mascota */
 class Mono extends Mascota{

 } // Fin de la clase Mono.

 # *** Fin de las CLASES *** #

 // Creamos un nuevo Perro:
 $Perro = new Perro('Fiel Amigo');

 // Imprimimos el numero de mascotas:
 echo '<p>Despues de crear un Perro, Ahora tenemos: ' . Mascota::obtenerContador() . ' Mascota(s)</p>';

 // Creamos un Gato:
 $Gato = new Gato('Bucky');
 echo '<p>Despues de crear un Gato, Ahora tenemos: ' . Mascota::obtenerContador() . ' Mascota(s)</p>';

 // Creamos otra Mascota:
 $Huron = new Huron('Fungo');
 echo '<p>Despues de crear un Huron, Ahora tenemos: ' . Mascota::obtenerContador() . ' Mascota(s)</p>';

 // Una tragedia ocurre!

 unset($Perro);

 echo '<p>Despues de que la tragedia ocurre, Ahora tenemos: ' . Mascota::obtenerContador() . ' Mascota(s)</p>';

 // Los monos son lindos:
 $Mono = new Mono('Toodles');
 echo '<p>Despues de crear un Mono, Ahora tenemos: ' . Mascota::obtenerContador() . ' Mascota(s)</p>';

 // Eliminamos todos los objetos:
 unset($Gato,$Huron,$Mono);
?>
</body>
</html>

La clase Mascota contiene un atributo estatico, que puede ser utilizado para contar el numero de objetos creados a partir de clases derivadas.

Constantes de Clase

Constantes de clase son como atributos estaticos en las que son accesibles para todas las instancias de la clase (o derivadas de la clase). Pero como cualquier otra constante, el valor nunca puede cambiar. Las constantes de clase se crean utilizando la palabra clave const, seguido del nombre de la constante (sin un signo dolar), seguido por el operador de asignacion y el valor de la constante:

class AlgunaClase{
        const PI = 3.14;
    }


Las constantes solo pueden asignar un valor como en este ejemplo. El valor no puede basarse en otra variable, y no puede ser el resultado de una expresion o una llamada de funcion. Las constantes, como atributos estaticos, tambien no se pueden acceder a traves de objeto. No se puede hacer esto:

$obj->PI;
o

$obj::PI;

Pero usted puede usar NombreClase::NOMBRE_CONSTANTE (por ejemplo: AlgunaClase::PI) en cualquier lugar.
Tambien se puede utilizar self::NOMBRE_CONSTANTE dentro de los metodos de la clase.



No hay comentarios:

Publicar un comentario en la entrada