Tercera y última parte del genial artículo PHP Orientado a Objetos for Beginners. En este artículo vamos a conocer sobre la visibilidad de los métodos y propiedades, cómo utilizar DocBlocks para documentar nuestros scripts y vamos a comparar la programación orientada a objetos con la procedimental, todo con ejemplos sencillos y fáciles de entender...
Importante: Si no tienes claro los conceptos de OOP con PHP, dirígete a la Parte 1 de este artículo. Allí comentamos los conceptos generales y hacemos una introducción a la Programación Orientada a Objetos con PHP. Para conocer más sobre Constructores y destructores, el uso de los métodos mágicos y las herencias, puedes acudir a la Parte 2 de esta serie.
Asignando la visibilidad de propiedades y métodos
Para un mayor control de nuestros objetos, métodos y propiedades hay que definir su visibilidad. Esto controla como y desde las propiedades y métodos pueden ser accedidas. Hay tres palabras claves sobre visibilidad: public, protected y private. Además de su visibilidad, un método o una propiedad puede declararse como estática, lo que nos permite acceder a ellas sin necesidad de instanciar la clase.
Nota: La visibilidad es una característica a partir de PHP 5. Puedes obtener más información sobre la compatibilidad de OOP con PHP4, leyendo esta página del manual de PHP.
Propiedades y métodos públicos
Todos los métodos y propiedades que hemos utilizado hasta ahora, han sido públicos. Esto significa que pueden accederse a ellos, desde cualquier lugar, tanto dentro como fuera de la clase.Propiedades y métodos protegidos
Cuando una propiedad o un método se declara como protegido, sólo se puede acceder a él dentro de la misma clase o en clases descendientes (clases que extienden a la clase que contiene el método protegido).Declara al método getProperty() en MyClass como protegido y trata de acceder a él desde fuera de la clase:
<?phpclass MyClass{
public $prop1 = "¡Soy la propiedad de una clase!";
public function __construct()
{
echo '¡La clase "', __CLASS__, '" fue iniciada!<br />';
}
public function __destruct()
{
echo '¡La clase "', __CLASS__, '" fue destruida!<br />';
}
public function __toString()
{
echo "Usando el método toString: ";
return $this->getProperty();
}
public function setProperty($newval)
{
$this->prop1 = $newval;
}
protected function getProperty()
{
return $this->prop1 . "<br />";
}
}
class MyOtherClass extends MyClass{
public function __construct()
{
parent::__construct();
echo "Un nuevo constructor en " . __CLASS__ . ".<br />";
}
public function newMethod()
{
echo "Desde un nuevo método en " . __CLASS__ . ".<br />";
}
}// Crea un nuevo objeto$newobj = new MyOtherClass;// Falla al usar el método protegidoecho $newobj->getProperty();?>
¡La clase "MyClass" fue iniciada!
Un nuevo constructor en MyOtherClass.
Fatal error: Call to protected method MyClass::getProperty() from context '' in /Applications/XAMPP/xamppfiles/htdocs/testing/test.php on line 55
<?phpclass MyClass{
public $prop1 = "¡Soy la propiedad de una clase!";
public function __construct()
{
echo '¡La clase "', __CLASS__, '" fue iniciada!<br />';
}
public function __destruct()
{
echo '¡La clase "', __CLASS__, '" fue destruida!<br />';
}
public function __toString()
{
echo "Usando el método toString: ";
return $this->getProperty();
}
public function setProperty($newval)
{
$this->prop1 = $newval;
}
protected function getProperty()
{
return $this->prop1 . "<br />";
}
}
class MyOtherClass extends MyClass{
public function __construct()
{
parent::__construct();
echo "Un nuevo constructor en " . __CLASS__ . ".<br />";
}
public function newMethod()
{
echo "Desde un nuevo método en " . __CLASS__ . ".<br />";
}
public function callProtected()
{
return $this->getProperty();
}
}// Crea un nuevo objeto$newobj = new MyOtherClass;// Usa el método protegido desde un método públicoecho $newobj->callProtected();?>
¡La clase "MyClass" fue iniciada!
Un nuevo constructor en MyOtherClass.
¡Soy la propiedad de una clase!
¡La clase "MyClass" fue destruida!
Propiedades y métodos privados
Una propiedad o método que es declarado como privado sólo es accesible desde dentro de la clase que lo define. Esto significa que incluso si una nueva clase extiende la clase que define la propiedad o método privado, este no estará disponible dentro de la clase hija.Para demostrarlo, declara getProperty() como privado en MyClass, y trata de llamar a callProtected() desde MyOtherClass:
<?phpclass MyClass{
public $prop1 = "¡Soy la propiedad de una clase!";
public function __construct()
{
echo '¡La clase "', __CLASS__, '" fue iniciada!<br />';
}
public function __destruct()
{
echo '¡La clase "', __CLASS__, '" fue destruida!<br />';
}
public function __toString()
{
echo "Usando el método toString: ";
return $this->getProperty();
}
public function setProperty($newval)
{
$this->prop1 = $newval;
}
private function getProperty()
{
return $this->prop1 . "<br />";
}
}
class MyOtherClass extends MyClass{
public function __construct()
{
parent::__construct();
echo "Un nuevo constructor en " . __CLASS__ . ".<br />";
}
public function newMethod()
{
echo "Desde un nuevo método en " . __CLASS__ . ".<br />";
}
public function callProtected()
{
return $this->getProperty();
}
}// Crea un nuevo objeto$newobj = new MyOtherClass;// Usa el método protegido desde un método públicoecho $newobj->callProtected();?>
¡La clase "MyClass" fue iniciada!
Un nuevo constructor en MyOtherClass.
Fatal error: Call to private method MyClass::getProperty() from context 'MyOtherClass' in /Applications/XAMPP/xamppfiles/htdocs/testing/test.php on line 49
Propiedades y métodos estáticos
Un método o propiedad declarado como estático puede ser accesible sin crear instancias de la clase; simplemente basta con proporcionar el nombre de la clase, el operador de resolución de alcance y el nombre de la propiedad o método.Una de las principales ventajas de utilizar propiedades estáticas es que mantienen sus valores almacenados durante la ejecución de todo el script.Para demostrar esto, agrega una propiedad estática llamada $count y un método estático llamado plusOne() a MyClass. Luego, configura un bucle do… while para imprimir el valor incrementado de $count siempre y cuando su valor sea menor a 10:
<?phpclass MyClass{
public $prop1 = "¡Soy la propiedad de una clase!";
public static $count = 0;
public function __construct()
{
echo '¡La clase "', __CLASS__, '" fue iniciada!<br />';
}
public function __destruct()
{
echo '¡La clase "', __CLASS__, '" fue destruida!<br />';
}
public function __toString()
{
echo "Usando el método toString: ";
return $this->getProperty();
}
public function setProperty($newval)
{
$this->prop1 = $newval;
}
private function getProperty()
{
return $this->prop1 . "<br />";
}
public static function plusOne()
{
return "La cuenta es " . ++self::$count . ".<br />";
}
}
class MyOtherClass extends MyClass{
public function __construct()
{
parent::__construct();
echo "Un nuevo constructor en " . __CLASS__ . ".<br />";
}
public function newMethod()
{
echo "Desde un nuevo método en " . __CLASS__ . ".<br />";
}
public function callProtected()
{
return $this->getProperty();
}
}
do
{
// Llama al método plusOne sin instanciar MyClass
echo MyClass::plusOne();
} while ( MyClass::$count < 10 );?>
Cuando cargues el script en el navegador, verás lo siguiente:
La cuenta es 1.
La cuenta es 2.
La cuenta es 3.
La cuenta es 4.
La cuenta es 5.
La cuenta es 6.
La cuenta es 7.
La cuenta es 8.
La cuenta es 9.
La cuenta es 10.
Comentando con DocBlocks
Aunque no es parte oficial de la POO, el comentar al estilo DocBlock es un método de documentación ampliamente aceptado para documentar clases. Además de proporcionar un estándar a los desarrolladores que escriben código, muchos programadas de desarrollo (SDKs) han adoptado esta metodología, tal es el caso de Eclipse o NetBeans, y es utilizado para generar code hints o sugerencias de código.Un DockBlock se define mediante el uso de un bloque de comentario que empieza con un asterisco adicional:
El poder real de DocBlock viene con la posibilidad de usar tags, que comienzan con el símbolo del arroba (@) seguido inmediatamente del nombre del tag y el valor del mismo. Las etiquetas o tag en DocBlock permiten a los desarrolladores definir al autor de un archivo, la licencia de una clase, la información de la propiedad o método y otra información útil.
Las etiquetas más utilizadas son las siguientes:
<?php/**
* Este es un DocBlock muy básico
*/?>
@copyright: Esto significa el año del copyright y el nombre del titular del copyright del elemento actual. El formato es 2010 Titular del Copyright.
@license: Esto enlaza a la licencia del elemento actual. El formato para la información de la licencia es http://www.example.com/path/to/license.txt Nombre de la licencia.
@var: Esto define el tipo y descripción de una variable o propiedad de una clase. El formato es tipo y descripción del elemento.
@param: Este tag muestra el tipo y descripción del parámetro de una función o método. El formato es el tipo $nombre_del_elemento y descripción del elemento.
@return: El tipo y descripción del valor de retorno de una función o método es provisto por este tag. El formato es el tipo y descripción del elemento.
Una clase de ejemplo comentada con DocBlock podría tener este aspecto:
<?php/**
* Una clase sencilla
*
* Esta es una larga descripción de la clase,
* la cual ocupará tantas lineas como sea necesario. Esta
* no será necesaria, mientras la descripción corta sea
* necesaria.
*
* Esta descripción puede dividirse en varios párrafos si la
* descripción merece mucha verborrea.
*
* @author Jason Lengstorf <jason.lengstorf@ennuidesign.com>
* @copyright 2010 Ennui Design
* @license <a href="http://www.php.net/license/3_01.txt" title="http://www.php.net/license/3_01.txt">http://www.php.net/license/3_01.txt</a> PHP License 3.01
*/class SimpleClass{
/**
* Una variable pública
*
* @var string almacena data para la clase
*/
public $foo;
/**
* Define $foo con un nuevo valor al instanciarse la clase
*
* @param string $val un valor requerido por la clase
* @return void
*/
public function __construct($val)
{
$this->foo = $val;
}
/**
* Multiplica dos números enteros
*
* Acepta un par de enteros y devuelve el
* producto de ambos.
*
* @param int $bat un número a ser multiplicado
* @param int $baz un número a ser multiplicado
* @return int el propudcto de los dos parámetros
*/
public function bar($bat, $baz)
{
return $bat * $baz;
}
}?>
Comparando la programación orientada a objetos y la procedimental
Realmente no hay una manera correcta o incorrecta de escribir código. Dicho esto, esta sección presenta un argumento de peso para adoptar un enfoque orientado a objetos en el desarrollo de software, especialmente en aplicaciones de gran tamaño.Razón 1: Fácil de implementar
Si bien puede se un poco desalentador al principio, la programación orientada a objetos en realidad ofrece un enfoque más fácil para manejar datos. Debido a que un objeto puede almacenar datos internos, no se necesita pasar variables de una función a otra función para que trabajen apropiadamente.Además, dado que varias instancias de la misma clase pueden existir simultáneamente, tratar con grandes conjuntos de datos es infinitamente más sencillo. Por ejemplo, imagina que tienes información de dos personas que están siendo procesados en un mismo archivo. Se necesitan los nombres, ocupaciones y edades.
El enfoque procedimental
Este es el enfoque procedimental de nuestro ejemplo:Cuando los ejecutes, el código nos mostrará lo siguiente:
<?phpfunction changeJob($person, $newjob)
{
$person['job'] = $newjob; // Cambiar el trabajo de la persona
return $person;
}
function happyBirthday($person)
{
++$person['age']; // Añade 1 a la edad de la persona
return $person;
}$person1 = array(
'name' => 'Tom',
'job' => 'Button-Pusher',
'age' => 34);$person2 = array(
'name' => 'John',
'job' => 'Lever-Puller',
'age' => 41);// Imprime los valores iniciales de las personasecho "<pre>Person 1: ", print_r($person1, TRUE), "</pre>";
echo "<pre>Person 2: ", print_r($person2, TRUE), "</pre>";// Tom obtiene un ascenso y cumple años$person1 = changeJob($person1, 'Box-Mover');$person1 = happyBirthday($person1);// John sólo cumple años$person2 = happyBirthday($person2);// Imprime los nuevos valores de las personasecho "<pre>Person 1: ", print_r($person1, TRUE), "</pre>";
echo "<pre>Person 2: ", print_r($person2, TRUE), "</pre>";?>
Person 1: Array
(
[name] => Tom
[job] => Button-Pusher
[age] => 34
)
Person 2: Array
(
[name] => John
[job] => Lever-Puller
[age] => 41
)
Person 1: Array
(
[name] => Tom
[job] => Box-Mover
[age] => 35
)
Person 2: Array
(
[name] => John
[job] => Lever-Puller
[age] => 42
)
Para limpiar este ejemplo, sería conveniente dejarle menos cosas al desarrollador. Sólo la información esencial para la operación debe necesitar ser transmitida a las funciones.
Aquí es donde la POO avanza y puede ayudarte a limpiar las cosas.
El enfoque POO
Este es el enfoque POO de nuestro ejemplo:<?phpclass Person{
private $_name;
private $_job;
private $_age;
public function __construct($name, $job, $age)
{
$this->_name = $name;
$this->_job = $job;
$this->_age = $age;
}
public function changeJob($newjob)
{
$this->_job = $newjob;
}
public function happyBirthday()
{
++$this->_age;
}
}// Crea dos personas nuevas$person1 = new Person("Tom", "Button-Pusher", 34);$person2 = new Person("John", "Lever Puller", 41);// Imprime su punto de inicioecho "<pre>Person 1: ", print_r($person1, TRUE), "</pre>";
echo "<pre>Person 2: ", print_r($person2, TRUE), "</pre>";// Da a Tom un ascenso y un cumpleaños$person1->changeJob("Box-Mover");$person1->happyBirthday();// John sólo obtiene un año más$person2->happyBirthday();// Imprime los valores finalesecho "<pre>Person 1: ", print_r($person1, TRUE), "</pre>";
echo "<pre>Person 2: ", print_r($person2, TRUE), "</pre>";?>
Person 1: Person Object
(
[_name:private] => Tom
[_job:private] => Button-Pusher
[_age:private] => 34
)
Person 2: Person Object
(
[_name:private] => John
[_job:private] => Lever Puller
[_age:private] => 41
)
Person 1: Person Object
(
[_name:private] => Tom
[_job:private] => Box-Mover
[_age:private] => 35
)
Person 2: Person Object
(
[_name:private] => John
[_job:private] => Lever Puller
[_age:private] => 42
)
En menor medida, esta diferencia puede no parecer mucha, pero conforme las aplicaciones crezcan en tamaño, POO reducirá significativamente la carga de trabajo, si se aplica correctamente.
Consejo: No todo tiene que ser orientado a objetos. Una función rápida que se ocupa de algo pequeño en un lugar o dentro de una aplicación no necesita tener que estar envuelta en una clase. Utiliza tu juicio para decidir que enfoque utilizar, el orientado a objetos o el procedimental.
Razón 2: Mejor organización
Otro beneficio de la POO es lo bien que se presta para ser empaquetado y catalogado. Cada clase en general se puede mantener en su archivo por separado, y si una convención de nomenclatura uniforme es utilizada, acceder a las clases será extremadamente simple.Supongamos que tenemos una aplicación con 150 clases que se llaman de forma dinámica a través de un archivo controlador en la raiz de nuestra aplicación. Todas las 150 clases siguen la convención de nomenclatura class.classname.inc.php y residen en la carpeta inc de nuestra aplicación.
El controlador puede implementar la función __autoload() de PHP para cargar de forma dinámica sólo las clases que van a necesitar ser llamadas, en lugar de tener que incluir los 150 archivos en el controlador:
<?php
function __autoload($class_name)
{
include_once 'inc/class.' . $class_name . '.inc.php';
}?>
Razón 3: Un mantenimiento más fácil
Debido a la naturaleza más compacta de la programación orientada a objetos, cuando se aplica correctamente; los cambios en el código son generalmente mucho más fácil de detectar y cambiar que en un código espagueti de implementación procedimental.Si un array de información obtiene un nuevo atributo, el enfoque procedimental podrá requerir (en el peor de los casos) que el nuevo atributo se añada en cada función que utilice el array.
Una aplicación POO podría actualizarse con la misma facilidad con la que se agrega una nueva propiedad y, a continuación, añade los métodos que tienen que ver con dicha propiedad.
Muchos de los beneficios cubiertos en esta serie de artículos son el producto de la POO en combinación con las prácticas de desarrollo DRY. Definitivamente, es posible crear un código procedimental que no nos de pesadillas; pero, es igualmente posible crear un código orientado a objetos terrible. Por eso, hemos intentado demostrar como combinar los buenos hábitos de programación relacionados con la programación orientada a objetos para generar un código limpio, que sea fácil de leer y mantener.
Conclusiones
En este momento, debes sentirte cómodo con el estilo de la programación orientada a objetos. Aprender POO es una muy buena forma de llevar tu programación al siguiente nivel. Cuando se implementa correctamente, la programación orientada a objetos te ayudará a leer y mantener el código fácilmente; y te salvará (y a los desarrolladores que trabajan contigo) de horas extra de trabajo. ¿Estas metido en algo que no ha sido tratado en esta serie de artículos? ¿Ya utilizas la POO y tienes algunos ejemplos para principiantes? ¡Compártelos en los comentarios!Via baluart.net
No hay comentarios:
Publicar un comentario