miércoles, 27 de mayo de 2009

Notas para iniciarse en PHP, Parte 6: Clases y objetos en PHP5


Un nuevo modelo de objetos

En la versión 5 de PHP se introdujo un nuevo modelo de objetos, rehaciendo completamente el anterior, para mejorar el funcionamiento y agregar nuevas características, en beneficio del programador por fortuna.

El cambio de modelos

Como mencionamos en el artículo anterior, la definición de una clase esta integrada por un conjunto de componentes que aparecen dentro de una estructura. Para definir una clase se utilizará nuevamente la palabra "class", seguida del nombre de la clase y la apertura y cierre de corchetes, dentro de los cuales se declararán los componentes de ésta. Hasta aquí no se ve diferencia con el modelo anterior sin embargo, durante la declaración de los componentes de la clase es cuando tenemos los primeros cambios, ya que ahora se toma en cuenta la visibilidad de los campos (también llamados propiedades en el manual) y los métodos, por ejemplo si tomamos la clase Carrito del artículo anterior y la migramos a la versión 5 del lenguaje tenemos un resultado como el que sigue:

class Carrito {
public $items; // Ítems en nuestro carrito de compras

// Agregar $num artículos de $artnr al carrito

public function agregar_item($artnr, $num) {
$this->items[$artnr] += $num;
}

// Tomar $num artículos de $artnr del carrito

public function retirar_item($artnr, $num) {
if ($this->items[$artnr] > $num) {
$this->items[$artnr] -= $num;
return true;
} elseif ($this->items[$artnr] == $num) {
unset($this->items[$artnr]);
return true;
} else {
return false;
}
}
}


Es la clase Carrito con los métodos que ya vimos antes, sin embargo tanto al único campo como a los métodos se les antepuso la palabra public, que es un indicador de visibilidad, esto se debe a que el modelo de objetos de PHP5 tenemos que indicar el tipo de visibilidad de los métodos y propiedades de nuestras clases, la cual especifica el contexto en el que estos componentes podrán ser utilizados. La declaración public indica que el método puede ser utilizado en el contexto en el que se instancie la clase, es decir donde se cree un objeto de esta clase, por ejemplo si instanciamos la clase Carrito:

$carrito = new Carrito;
$carrito->agregar_item("10", 1);
$carrito->agregar_item("8", 3);
$carrito->agregar_item("110", 4);
$carrito->retirar_item("8", 2);


Podemos acceder a los métodos agregar_item o retirar_item debido a que su indicador de visibilidad es public, de haber utilizado protected o private esto no habría sido posible, ya que protected determina que los métodos y propiedades solo pueden ser utilizados dentro de la definición de la propia clase y en la de sus clases extendidas, mientras que private restringe el uso de los métodos y propiedades únicamente a la declaración de la misma clase .

El concepto de visibilidad se introdujo en esta versión es una de las principales diferencias que encontraremos durante el trabajo con las clases, y lo primero a lo que debemos adaptarnos al migrar nuestra programación, el resto del trabajo consiste en conocer las nuevas funcionalidades que ofrece PHP para este modelo.

Instancia de una clase

Como se vio en el ejemplo anterior, la instanciación de clases se sigue haciendo de la misma manera que en la versión 4, solo hay que notar que algunas autores recomiendan que aunque no se pasen parámetros al constructor se incluya el paréntesis durante la instanciación, es decir:

$carrito = new Carrito();
Lo cual es totalmente opcional.

Herencia

El mecanismo para la herencia sigue siendo la extensión de clases con "extends" como en la versión 4.

Autocarga de objetos

Esta es una facilidad que brinda PHP para acceder a los archivos fuente de nuestras clases, que es útil cuando el nombre del archivo es el mismo que el de la clase que contiene. Anteriormente se tenía que realizar un include para cada archivo, mientras que ahora se cuenta con una función que puede hacer ese trabajo por nosotros.

Si declaramos en nuestro programa la función __autoload() PHP la llamará automáticamente cada vez que se haga una nueva instancia a una clase que no ha sido incluida aún, esta función recibe el nombre de la clase como parámetro y dentro de ella podemos realizar la inclusión del archivo, por ejemplo:

<?php
function __autoload($class_name) {
require_once $class_name . '.php';
}

$obj = new MyClass1();
$obj2 = new MyClass2();
?>


En este ejemplo no existe dentro del programa la definición de la clase MyClass1, entonces al hacer la instancia PHP ejecutará por si solo la función __autoload() y le pasará como parámetro el nombre de la clase que estamos llamando, es decir que la función recibirá "MyClass1" en su parámetro $class_name, entonces teniendo el nombre de la clase y sabiendo que es el mismo nombre del archivo que la contiene, podemos incluir este archivo con la función require_once.

Constructores y destructores

La declaración de un constructor ahora es diferente, ya no se utiliza el nombre de la clase para identificarlo sino el nombre especial __contruct, de modo que la función que declaremos con este identificador será llamada cada vez que se cree un nuevo objeto de nuestra clase:

<?php
class BaseClass {
function __construct() {
print "In BaseClass constructor\n";
}
}

class SubClass extends BaseClass {
function __construct() {
parent::__construct();
print "In SubClass constructor\n";
}
}

$obj = new BaseClass();
$obj = new SubClass();
?>


El ejemplo anterior nos muestra no solo la declaración de un constructor de un clase sino también que el constructor de una clase padre no es llamado implícitamente por el constructor de sus hijos, por lo que tiene que debe ser llamado explícitamente con partent::__contruct(). La principal ventaja de utilizar el mismo nombre para todos los constructores, es que al momento de cambiar el nombre de una clase no tendremos que hacer lo mismo con su contructor.

El método __destruct() es el destructor de la clase, este es un nuevo tipo de método que se incluyó como una de las novedades de PHP5, el cual es ejecutado cuando todas las referencias al objeto de la clase sean removidas, o cuando el objeto sea explícitamente destruido, o cuando llegue la finalización de un programa.

<?php
class MyDestructableClass {
function __construct() {
print "In constructor\n";
$this->name = "MyDestructableClass";
}

function __destruct() {
print "Destroying " . $this->name . "\n";
}
}

$obj = new MyDestructableClass();
?>


Este método tampoco es llamado implícitamente en los destructores de las clases hijo, por lo que de querer ejecutar el destructor del padre debería llamarse con parent::__destruct().

Elementos estáticos y constantes

Dentro de los componentes de una clase pueden existir miembros estáticos, es decir propiedades y métodos accesibles fuera de la definición de la propia clase que no necesitan de un objeto para ser utilizados, sino que pueden accederse directamente con el operador de resolución de contexto '::'. Estos componentes estáticos no pueden ser redefinidos en la extensión de una clase.

<?php
class Foo {
public static $prop = "Propiedad estática";
public static function aStaticMethod() {
echo "Método estático que es accesible sin un objeto";
}
}

Foo::aStaticMethod();
echo Foo::$prop;
?>


Para acceder a una propiedad o método estático dentro de la definición de la misma clase, se utiliza la palabra 'self' junto con el operador de resolución de contexto '::' como se indica:

class Foo
{
public static $my_static = 'foo';

public function staticValue() {
return self::$my_static;
}
}


También pueden existir propiedades constantes es decir, valores que no pueden ser alterados y que tienen un identificador para acceder a ellos. Las constantes difieren de las variables normales en que no se usa el símbolo $ para declararlas o usarlas. Para leer sus valores desde el contexto de la definición de la clase se utiliza la palabra 'self' seguida del operador '::' junto con el identificador de la constante. Desde el contexto de la instancia no se puede acceder a las contantes como una propiedad de los objetos, sino que se accede directamente a ellas a través de la clase con el operador de resolución de contexto '::'.

<?php
class MyClass
{
const constant = 'constant value';

function showConstant() {
echo self::constant . "\n";
}
}

echo MyClass::constant . "\n";

$class = new MyClass();
$class->showConstant();
// echo $class::constant; No es una operación permitida
?>
Su modo de acceso es similar al de las propiedades estáticas como se puede ver, con la diferencia de que las propiedades constantes no pueden ser alteradas. En ambos casos si deseamos acceder a una propiedad estática o constante de la clase padre desde un método del hijo, debemos utilizar la palabra 'partent', tal como se vió su uso en las secciones anteriores.

Abstracción de datos e interfaces

Otra de las novedades de esta versión son las clases abstractas, las cuales nos permiten crear clases no instanciables que funcionan como base para las clases hijo que las extenderán. Las clases abstractas pueden contener métodos abstractos, es decir métodos de los que solo aparece su declaración en la clase base y que forsozamente tienen que ser definidos en las clases hijo.

Las clases abstractas son útiles cuando tenemos un grupo de clases que compartirán un comportamiento en común y al mismo tiempo implementarán funcionalidades particulares, para lo cual podemos crear una clase base que indique la estructura general que tendrán todos las clases extendidas. La diferencia con el tipo de herencia común es que las clases abstractas se utilizarán cuando no necesitemos implementar el comportamiento de la clase base dentro de nuestros programas.

Siguiendo con la revisión también nos encontramos que existen interfaces, que consisten en estructuras similares a las clases abstractas ya que no pueden instanciarse, y dentro de su definición también hayamos propiedades y métodos, pero tienen la gran diferencia de que en una interface solo se declaran los métodos, estos nunca se definen, de esta tarea se encargarán las clases que las implementen. Como se pueden dar cuenta, mencione que las clases implementarán las interfaces, y es que aquí se habla de implementación en lugar de extensión, a pesar de ser mecanismos similares se hace la diferencia, ya que en el caso de las interfaces las clases que las implementan nunca heredan un método funcional. Entonces estas estructuras especifican qué métodos deberán poseer las clases que los implementen, y dejarán los detalles de la implementación a estas clases.

Las interfaces tienen gran utilización dentro de los lenguajes orientados a objetos para estandarizar ciertas funciones, al establecer los comportamientos que deberán seguir los objetos que implementen alguna funcionalidad particular. Por ejemplo, dentro de Java se utilizan para implementar el manejo de eventos del usuario, ya que si deseamos que alguna de nuestras clases pueda capturar algún evento del teclado o ratón, necesitan implementar una interfase que proporciona el lenguaje para el manejo de estos eventos, de forma que sin conocer los detalles sobre el manejo de eventos, sabremos que métodos deberemos implementar dentro de nuestras clases para utilizarlo. Algo similar puede verse en otros lenguajes, incluyendo a PHP, para crear frameworks de programación, ya que de esta forma podemos establecer que comportamiento deberá tener una clase que desee implementar cierta funcionalidad contemplada en el armazón.

La palabra reservada 'final'

Cuando se desea que las clases hijo no puedan redefinir un método o modificar el valor de una propiedad de la clase padre, se puede utilizar la palabra reservada final durante su declaración, con la cual PHP interpretará que este valor no podrá ser alterado fuera del contexto de su clase, lanzando un error de querer hacerlo.

Otras funcionalidades

PHP ofrece además de lo anterior algunas opciones adicionales que podemos utilizar con nuestras clases para obtener ciertas facilidades, como son:

El acceso a miembros y sobrecarga de métodos. Se usan para registrar nuevas propiedades en los objetos en tiempo de ejecución y para implementar la sobrecarga de métodos.
La interacción con objetos. Últil para desplegar las propiedades de un objeto desde un foreach.
Patrones. Sirven para implementar funcionalidades comunes en nuestros programas que nos ahorren tiempo de programación.
Clonación de objetos. Nos permite hacer replicas de nuestros objetos.

Y con esta última revisión termino con mis anotaciones sobre el modelo de objetos de PHP5, ahora lo que queda por ver es la forma en la que podemos utilizar todo lo aprendido interactuando con las interfaces de usuario, para lo que revisaremos la lectura de datos de formularios, el manejo de sesiones y cookies, y algunas funciones que hacen varias cosas interesantes.

Hasta la próxima.



0 comentarios:

Publicar un comentario