jueves, 21 de mayo de 2009

Notas para iniciarse en PHP, Parte 5: Clases y objetos en PHP4


Todo gran viaje comienza con un solo paso

La utilización de clases y objetos

PHP nos permite programar tanto de manera procedimental como con orientación a objetos, es en está última donde surgen los conceptos de clases y objetos, a los cuales nos enfocaremos en este artículo. En el viejo estilo estructurábamos nuestros programas en un conjunto de funciones que utilizábamos dentro de las secuencias de ejecución, en la programación orientada a objetos nuestras aplicaciones constarán de un conjunto de elementos llamados objetos, los cuales interactúan entre sí por medio de sus métodos. Un método es similar a una función en programación estructurada, solo que éste se encuentra asociado a un objeto específico, el cual utiliza los métodos para implementar su funcionalidad particular. Todos los objetos están basados en la definición de una clase, la cual puede verse como la estructura que especifica las características y el comportamiento que tendrá el objeto; todo objeto debe pertenecer a una determinada clase, la clase entonces es una entidad pasiva que define que hará el objeto, mientras el objeto es una entidad activa que realizará un conjunto de tareas dentro de nuestros programas. Si por ahora no ha quedado muy clara esta definición quizás con la implementación en el lenguaje se entenderá mejor.

Clases y objetos en PHP4

En PHP una clase es una colección de variables y funciones que trabajan con estas variables. El concepto de clase y objeto se introdujo en este lenguaje a partir de su versión 4, contando ya con conceptos como la herencia y los constructores, componentes que representan el mínimo necesario para considerarla orientada a objetos, sin embargo este modelo se queda corto si lo comparamos con otros lenguajes basados en objetos más maduros y versátiles como Java o C#. El modelo de la versión 4 es más parecido al de otros lenguajes basados en scripts como Javascript o Actionscript. Por todo lo anterior en la versión 5 del lenguaje se introdujoun nuevo modelo de objetos, que incluyera un mejor funcionamiento y más opciones, para el programador, pero aún así, las bases para el trabajo con clases y objetos siguen siendo los de la versión 4, por lo que es importante al iniciarse en este tema dar un repaso a las características que ofrece esta versión, además de que muchas bibliotecas libres que encontramos por la red siguen utilizándola.

Definición de una clase

La definición de una clase en PHP se realiza por medio de la palabra reservada class seguida de la apertura y cierre de llaves utilizada en las estructuras, dentro de estas llaves se escriben las variables y funciones que contiene una clase. Siguiendo una sintaxis como la que sigue:
class NombreDeLaClase {
...
}


El siguiente ejemplo nos muestra una implementación para esta sintaxis:
<?php
class Carrito {

var $items; // Ítems en nuestro carrito de compras

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

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

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

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;
}
}
}
?>

En este ejemplo aparece la definición de la clase Carrito, que posee una variable que contendrá un arreglo asociativo, además la clase dispone de dos funciones o métodos para agregar o eliminar items del arreglo, en un sentido más abstracto la clase representa un objeto del mundo real, en este caso un carrito de compras, al que le podemos agregar o quitar productos.

Las variables o campos (como se les llama en el lenguaje Java) de las clases se identifican anteponiendo la palabra reservada var, estos campos se pueden acceder desde cualquiera de las funciones de la clase, como si se tratara de variables globales dentro del contexto de la definición de la clase, anteponiendo al nombre del campo la expresión $this->, lo cual es muy importante ya que de otra forma PHP interpretara que se trata de una variable local de las funciones en las que se está haciendo referencia. Las funciones que aparecen, que tienen la misma definición que en la programación estructurada que vimos en artículos anteriores, reciben el nombre de métodos, que es el término que emplearemos de ahora en adelante, ya que es el nombre que reciben en la programación orientada a objetos, debido a que a través de ellos se define el comportamiento de los objetos que se crean con las definiciones de nuestras clases. Para accesar a un método contenido en la definición de la clase también se utliza la expresión $this-> seguida de la llamada al método, por ejemplo si se incluyera un tercer método en la clase Carrito el cual necesitara utilizar alguno de los otros:
   //Vaciar el carrito de compras eliminando todos los artículos

function vaciar_carrito() {
foreach ($this->items as $artnr=>$num)
$this->retirar_item($artnr, $num);
}
En el ejemplo, el nuevo método vaciar_carrito() utilizará la funcionalidad de retirar_item() para eliminar todos los elementos del arreglo.

Los métodos le permiten a los objetos interactuar con su entorno, para lo cual es necesario primero crear un objeto con la definición de la clase, lo cual es llamado instanciar una clase, y se hace de la siguiente forma:

$objeto = new NombreDeLaClase;


La instancia de la clase se realiza con la expresión new seguida del nombre de la clase y está debe ser asignada a una variable, la cual será el objeto creado a partir de esta instancia. El nombre de la clase puede llevar paréntesis si así lo deseamos, pero su uso es opcional a menos que la definición del constructor de la clase indique que tiene que recibir algún de valor, para lo cual se vuelve obligatorio el uso de paréntesis en la instanciación, el tema de los constructores lo veremos más adelante.

Siguiendo con el ejemplo del carrito de compras la instancia de la clase Carrito se realizaría de la siguiente manera:
$carrito = new Carrito;
$carrito->agregar_item("10", 1);

$otro_carrito = new Carrito;
$otro_carrito->agregar_item("0815", 3);

En el ejemplo anterior se realizan dos instancias de la clase Carrito por medio de la expresión new Carrito, con lo cual se crean los objetos $carrito y $otro_carrito, los cuales una vez creados pueden acceder a los métodos de esta clase -tanto agregar_item como retirar_item).

Herencia

Las clases pueden tener puntos de intersección en su definición, como suele ser el caso de compartir campos o métodos similares o idénticos, en ese caso sería un gasto de tiempo innecesario volver a escribir estos métodos y campos en la definición en cada clase en las que se vayan a utilizar, para facilitar la solución de este problema PHP permite la herencia entre la clases, la cual consiste en que a partir de una clase original se pueden crear otro grupo de clases que contengan los métodos y campos de la primera, pero sin tener que reescribirlos en cada una de ellas, la clase original es llamada padre mientras que a las clases derivadas se les llama hijos, entonces estos componentes son heredados de la clase padre a las clases hijo, con lo cual se consigue que compartan la definición de los campos y métodos del padre, pudiendo además incorporar nuevas funcionalidades a las ya heredadas. PHP implementa la herencia gracias a un mecanismo llamado extensión, este nombre viene del hecho de que por lo general las clases hijo extienden la funcionalidad de la clase padre, al redefinir antiguos métodos o incluir nuevos métodos y campos. Las clases extendidas o clases hijo deben incluir en su definición la palabra reservada 'extends'.
<?php
class Carrito_Con_Nombre extends Carrito {
var $duenyo;

function definir_duenyo ($nombre) {
$this->duenyo = $nombre;
}
}
?>

En este ejemplo se define una nueva clase llamada Carrito_Con_Nombre el cual extiende la clase Carrito, ya que hereda de ella todos sus métodos y campos y, además cuenta con un campo y método propio, entonces un objeto de la clase Carrito_Con_Nombre consistirá en un carrito con los métodos agregar_item como retirar_item que además posee un dueño que puede ser definido con el método definir_duenyo, como podemos verlo al instanciar la nueva clase en el siguiente ejemplo:
<?php
$carrito_n = new Carrito_Con_Nombre; // Crear un carrito con nombre
$carrito_n->definir_duenyo("kris");// Nombrar el carrito
print $carrito_n->duenyo; // imprimir el nombre del dueño
$carrito_n->agregar_item("10", 1); // (funcionalidad heredada de carrito)
?>


Vemos que el objeto posee los métodos de la clase padre y los métodos de su propia clase.

Constructores

El constructor de una clase es un método especial que podemos incluir en su definición, el cual es llamado al momento de hacer una nueva instancia de la clase con new, por ejemplo podemos definir un constructor para una nueva clase que extienda a Carrito de la siguiente forma:
<?php
class Auto_Carrito extends Carrito {
function Auto_Carrito() {
$this->agregar_item("10", 1);
}
}
?>


El constructor debe llevar el mismo nombre de la clase a la que pertenece, como vemos en el ejemplo, el cual nos indica que al crearse una nueva instancia de la clase Auto_Carrito, se incluirá por defecto un primer item en el arreglo al emplear el método heredado agregar_item dentro del constructor.

Un constructor puede además incluir uno o más parámetros en su definición, de forma que podamos enviar valores a este método al instanciar un nuevo objeto de la clase:

<?php
class Constructor_Carrito extends Carrito {
function Constructor_Carrito($item = "10", $num = 1) {
$this->agregar_item ($item, $num);
}
}

// Comprar lo mismo de antes.
$carrito_predeterminado = new Constructor_Carrito;

// Comprar esta vez en serio...
$carrito_diferente = new Constructor_Carrito("20", 17);
?>


En el ejemplo dentro de la definición se utilizaron parámetros con valores por defecto, por lo cual no es necesario que en la primera instancia($carrito_predeterminado) se incluyan nuevos valores, para obtener un resultado diferente vemos que en la segunda instancia($carrito_diferente) ya se incluyen los valores dentro de los paréntesis, de forma que ahora el constructor los incluirá dentro del arreglo asociativo.

Los constructores son muy útiles si deseamos asegurarnos que los campos nuestras clases tengan un valor inicial al crearse una nueva instancia de estas.

Operador de resolución de contexto (::)

El operador :: nos permite acceder a un campo o método de una clase sin la necesidad de tener que hacer una instanciación, como sucede en el siguiente ejemplo:
<?php
class A {
function ejemplo() {
echo "Soy la función original A::ejemplo().<br />\n";
}
}

class B extends A {
function ejemplo() {
echo "Soy la función redefinida B::ejemplo().<br />\n";
A::ejemplo();
}
}

// no hay un objeto de la clase A.
// esto imprime
// Soy la función original A::ejemplo().<br />
A::ejemplo();

// crear un objeto de clase B.
$b = new B;

// esto imprime
// Soy la función redefinida B::ejemplo().<br />
// Soy la función original A::ejemplo().<br />
$b->ejemplo();
?>


El ejemplo anterior llama al método ejemplo() en la clase A, pero no hay un objeto de la clase A, así que no podemos escribir $a->ejemplo() o algo semejante. En su lugar llamamos ejemplo() como un 'método de clase', es decir, un método de la clase misma, no de un objeto de tal clase.

Acceso a los campos y métodos del padre (parent)

Al utilizar herencia o extensión de clases con 'extends', ya nos es posible acceder a los métodos y campos de la clase dentro de los nuevos métodos que definimos en las clases hijo, sin embargo, al realizar una refinación o especialización de un método ya existente, a veces resulta necesario utilizar la funcionalidad del método original antes de incluir nuestros cambios.

Para acceder a un método del padre se utiliza la palabra reservada 'parent' seguida del operador de resolución de contexto (::), acompañado con la referencia al método deseado.

En el siguiente ejemplo se muestra la refinación del método ejemplo() en el cual además de incluirse una nueva funcionalidad se hace uso de la funcionalidad del método original:

<?php
class A {
function ejemplo() {
echo "Soy A::ejemplo() y ofrezco funcionalidad básica.<br />\n";
}
}

class B extends A {
function ejemplo() {
echo "Soy B::ejemplo() y ofrezco funcionalidad adicional.<br />\n";
parent::ejemplo();
}
}

$b = new B;

// Esto hace la llamada a B::ejemplo(), la cual llama a su vez a A::ejemplo().
$b->ejemplo();
?>


A pesar de que los hijos heredan los métodos del padre, al volverlos a definir se hace necesaria una forma para diferenciar el método original del refinado, ya que si nos fijamos en el ejemplo, de haber utilizado la expresión $this->ejemplo() PHP interpretaría que se hace referencia al método del hijo, el cual ya cambió y no se obtendría el resultado deseado, además de que se crearía una problema recursivo.

Con este último ejemplo es que concluye el último artículo de esta semana, para el siguiente veremos las diferencias que tiene la versión 5 del lenguaje para el manejo de objetos y las nuevas funcionalidades que nos ofrece.

Hasta la próxima.

0 comentarios:

Publicar un comentario