El objeto
Introducción
El objetivo de este capítulo no es explicar todas las complejidades de la programación orientada a objetos (POO), sino ver las bases para que pueda programar un código simple o entender cómo funciona un código objeto que ya existe.
Por ahora, el código que se muestra es de carácter procedimental, es decir, puede crear las funciones a las que llama cuando las necesita, todo en orden cronológico.
En POO, casi todo son objetos y todos los objetos interactúan entre sí.
Un objeto tiene unas características, que son los atributos, y unas acciones, que son los métodos.
Por ejemplo, el objeto Animal tiene como atributos el color y el peso, y tiene como métodos moverse y comer.
Para construir estos objetos, debe utilizar una clase.
Las clases
1. Introducción
Una clase sirve para fabricar objetos partiendo de un modelo. Estos objetos tienen sus propios atributos y ciertos métodos.
Por ejemplo, la clase Animal tiene los atributos color y peso y los métodos comer y moverse.
Cuando se crean ejemplares de animales en la clase Animal, se crea una instancia de esta clase. Crear una instancia de una clase significa que se crea un objeto de un tipo determinado (Animal) con ciertos atributos (color, peso).
Creación de una clase en PHP:
<?php
class Animal // palabra clave class seguida del nombre de la clase.
{
// Declaración de atributos y métodos.
}
?>
Se recomienda crear una clase por cada archivo PHP que tenga el mismo nombre que la clase.
2. La encapsulación
Todos los atributos en POO deben estar ocultos a otras personas que utilizan sus clases. Si trabaja en equipo y crea la clase Animal, los otros programadores no van a poder cambiar directamente los atributos de su clase. De esta forma, los atributos color y peso se ocultan a otras clases; se declaran privadas. La clase Animal tiene métodos para leer o escribir estos atributos. Este es el principio de encapsulación, que permite proteger el código cuando trabaja en equipo.
La clase Animal, que tiene como propiedades el color y el peso, dispone de un método para modificar su color, un método para leer el color, un método para modificar su peso, un método para leer su peso, así como otros métodos tales como comer o moverse (ver la sección Actualizar y leer los atributos de la instancia, más adelante en este capítulo).
3. Visibilidad de los atributos y de los métodos
Hay tres tipos de palabras clave para definir la visibilidad de un atributo o de un método:
-
private: solo el código de su clase puede ver y acceder a este atributo o método.
-
public: todas las demás clases pueden acceder a este atributo o método.
-
protected: solo el código de su clase y de sus subclases pueden acceder a este atributo o método.
Las subclases se tratarán en una próxima sección.
Creación de una clase en PHP con sus atributos:
<?php
class Animal // palabra clave seguida del nombre de la clase.
{...
La herencia
1. Introducción
La herencia es un concepto muy importante en POO. Permite reutilizar el código de una clase sin necesidad de volver a escribirlo.
Una clase hija hereda de una clase madre, es decir, accede a todos los atributos y los métodos públicos de la clase madre.
Por ejemplo, la clase Mamífero hereda de la clase Animal, y la clase Coche hereda de la clase Vehículo.
Si la clase A es una subcategoría de la clase B, entonces puede hacer que la clase A (Mamífero o Coche) herede de la clase B (Animal o Vehículo).
En el siguiente esquema puede observar como las clases Pez y Gato son ejemplos de herencia de la clase Animal.
Para crear la clase Pez que hereda de la clase Animal, debe utilizar la palabra clave extends entre el nombre de la clase hija y el nombre de la clase madre.
Cree un archivo Pez.class.php y escriba el siguiente código:
<?php
class Pez extends Animal
{
}
?>
Ahora añada un atributo privado que corresponda a la variable vive_en_el_mar y los accesos get y set. Para terminar, el método público nadar().
<?php
class Pez extends Animal
{
private $vive_en_el_mar; //tipo de pez
//accesos
public function getType()
{
if ($this->vive_en_el_mar){
return "vive_en_el_mar";
}
else if ($this->vive_en_el_mar===falso){
return "no_vive_en_el_mar";
}else {return "";}
}
public function setType($vive_en_el_mar)
{
$this->vive_en_el_mar = $vive_en_el_mar;
//escrito en el atributo vive_en_el_mar
}
//método
public function nadar()
{
echo...
Las clases abstractas
Las clases abstractas se escriben con la palabra clave abstract delante de la palabra class. Una clase abstracta no se puede instanciar, es decir, no permite crear una instancia. Puede escribir métodos abstractos, que son métodos donde solo escribe la firma precedida por la palabra clave abstract : abstract visibilidad function nombre_método (atributo tipo_atributo...). También puede implementar métodos normales en las clases abstractas. Estas clases solo sirven para obligar, a las clases que heredan de la clase abstracta a reemplazar los métodos abstractos declarados en la clase abstracta y para factorizar los métodos implementados.
En el siguiente ejemplo, la clase Animal es abstracta, ya que no se quiere crear (instanciar) animales, sino peces o gatos.
Añada también un método abstracto respira() en la clase Animal:
<?php
abstract class Animal
{
// Declaración de atributos
private $color = "gris";
private $peso = 10;
//constantes de clase
const PESO_LIGERO = 5;
const PESO_MEDIO = 10;
const PESO_PESADO = 15;
// Declaración...
Sustitución de métodos abstractos
A partir de PHP 7.2, es posible sustituir un método abstracto en una clase que hereda de otra clase abstracta.
Por ejemplo:
<?php
abstract class Animal {
//declaración del método abstracto correr
abstract function correr(int $kilometro);
}
abstract class Perro extends Animal {
//sustitución del método abstracto correr
abstract function correr($kilometro); //Tipo omitido
}
?>
Los tipos de parámetros de los métodos sustituidos tanto en las clases abstractas como en las interfaces pueden omitirse ya que dichos tipos son contravariantes.
Las interfaces
Las interfaces son similares a las clases abstractas. No se pueden instanciar, pero no puede implementar ningún método en ellas. Tampoco puede declarar variables en las interfaces. Por tanto, estas clases sirven únicamente para obligar a las clases que heredan de la interfaz a redefinir los métodos declarados en la interfaz. Estos métodos son obligatoriamente públicos. Una interfaz se declara con la palabra clave interface:
Este es el código de la interfaz acción:
<?php
interface acción {
function correr();
function comer();
}
?>
Para señalar que una clase implementa una o varias interfaces, debe añadir la palabra clave implements seguida del nombre de las interfaces.
Este es un ejemplo en el que se señala que la clase Gato implementa la interfaz acción:
<?php
class Gato extends Animal implements acción
{
...
//métodos de la interfaz
function correr() {
echo "El gato corre.<br />";
}
function comer() {
echo "El gato come.<br />";
} ...
Las clases finales
Cuando una clase es final, no se puede crear la clase hija que hereda de esta clase. Esto tiene poco interés práctico.
Debe añadir la palabra clave final delante de la palabra clave class.
Por ejemplo, si no crea una clase que hereda de la clase Pez, puede escribir final:
<?php
final class Pez extends Animal
{
private $vive en el mar; //tipo de pez
//accesos
...
//método
public function nadar()
{
echo "Nado <br />";
}
public function respira()
{
echo "El pez respira.<br />";
}
}
?>
También puede declarar los métodos finales. Estos métodos no se podrán sustituir.
En la sección dedicada a la sustitución hemos visto un ejemplo donde el método comer_animal(Animal $animal_comido) se ha sustituido en la clase Pez. Si este método era final en la clase Animal:
final public function comer_animal(Animal $animal_comido) ...
Los métodos mágicos
Un método mágico es un método al que se llama automáticamente cuando se produce un acontecimiento.
Por ejemplo __construct es un método mágico. Se ejecuta automáticamente cuando instancia la clase que contiene __construct.
Los métodos mágicos __get y __set permiten leer o modificar los atributos que no existen y en los que el acceso está prohibido.
Retome el ejemplo al inicio del capítulo con la clase Animal:
Esta vez, el atributo color es privado y el atributo peso es público:
<?php
class Animal
{
// Declaración de atributos
private $color = "gris";
public $peso = 10;
public function __construct($color, $peso) // Constructor
//que solicita 2 argumentos.
{
echo 'Llamada al constructor.<br />';
$this->color = $color; // Inicialización del color.
$this->peso = $peso; // Inicialización del peso.
}
//métodos públicos
public function comer ()
{
}
public function moverse ()
{
}
}
?>
Cuando crea una instancia de la clase Animal, puede acceder al atributo peso porque es público, pero no al atributo color porque es privado.
La página uso.php es:
<?php
//carga de clases
include('Animal.class.php');
//instanciar la clase Animal
$perro = new Animal("gris",8);
$perro->color = "negro";
echo $perro->color."<br />";
?>
Muestra un error porque intenta acceder directamente al atributo color, que es privado:
Llamada al constructor. |
Fatal error: Cannot access private property Animal::$color in C:\Program Files\EasyPHP-DevServer-15.9VC11\data\localweb\Objeto\ uso.php... |
Las clases anónimas
Esta característica, que llegó con PHP 7, permite utilizar una clase sin tener que definirla completamente. Esta clase no tiene nombre y se utiliza una sola vez. Esto se consigue mediante la instrucción new class.
Por ejemplo:
//creación de la clase anónima
$clase_anónima = new Class {
public $mensaje = "Hola";
public function getMessage() {
return "Buenos días";
}
};
echo $clase_anónima->getMessage();
Da como resultado:
Buenos días. |
Otro ejemplo que muestra la diferencia entre PHP 5.6 y PHP 7:
//Primera solución antes de PHP 7
//creación de la clase Mensaje
class Message
{
public function Mostrar_mensaje($msg)
{
echo $msg;
}
}
class Util
{
public $mensaje;
public function setMessage(Mensaje $mensaje) {
$this->mensaje = $mensaje;
}
}
$util = new Util();
$util->setMessage(new...
Los traits
Esta característica, que llegó con PHP 5.4, permite reutilizar código en dos clases independientes. Su sintaxis es:
trait nombre_del_trait {
//definición de las propiedades y métodos.
}
Tome el ejemplo de dos clases independientes Factura e Indemnización que tienen un método Cálculo_impuestos_iva.
<?php
//declaración del trait
trait MiTrait
{
public function Cálculo_impuestos_incluidos($importe)
{
return $importe*1.2; //devuelve el importe impuestos incluidos
}
}
class Factura
{
use MiTrait;
}
class Indemnización
{
use MiTrait;
}
$factura = new Factura;
//muestra el importe impuestos incluidos de la factura
echo $factura->Cálculo_impuestos_incluidos(10)."<br />";
$indemnización = new Indemnización;
//muestra el importe impuestos incluidos de la indemnización
echo $indemnización->Cálculo_impuestos_incluidos(20);
?>
Da como resultado:...
Uniform Variable Syntax
Esta sintaxis de variable uniforme le permite resolver algunas incoherencias en la evaluación de las expresiones. A partir de PHP 5.6, es posible acceder a una propiedad con esta sintaxis:
$obj->{$properties['name']};
Por ejemplo:
<?php
//Declaración de la clase Objeto
class Objeto { public $color ="rojo"; }
//Instanciación de la clase Objeto
$obj = new Objeto();
$properties['name'] = "color";
echo $obj->{$properties['name']};
?>
Da como resultado:
rojo |
Efectivamente, $obj->{$properties[’name’]} pasa a escribirse $obj->color.
A partir de PHP 7, es posible adjuntar llamadas estáticas.
Por ejemplo:
<?php
//Declaración de la clase 1
class clase1 { static $nombre1 = 'clase2'; }
//Declaración de la clase 2
class clase2 { static $nombre2 = 'Hola'; }
//Declaración del método que permite mostrar Hola
clase2::$nombre2 = function () { echo "¡Hola!"; };
$clase1 = 'clase1';
//Llamada estática
($clase1::$nombre1::$nombre2)();
?>
Da como resultado:
¡Hola! |
Los espacios de nombres
Cuando trabaja en proyectos grandes en equipo, es útil modularizar las clases y las funciones. De esta manera, cada desarrollador puede trabajar con su propio módulo. Desde PHP 5.3, las namespaces (espacio de nombres), permiten esta modularización. Un namespace es una especie de carpeta virtual en la que almacena sus objetos. De esta manera es posible usar clases o funciones con el mismo nombre, en namespaces diferentes.
Un namespace se declara con la palabra clave namespace, seguido de su nombre, al inicio del archivo.
Por ejemplo:
espacio_nombres.php
<?php
// Definición del espacio de nombres.
namespace Biblioteca;
// Definición de una constante.
const PI = 3.1416;
// Definición de una función.
function miFuncion() {
echo "Hola <br />";
}
// Definición de una clase.
class miClase {
/*
...
*/
}
?>
Uso_espacio_nombres.php:
<?php
include('espacio_nombres.php');
Biblioteca\miFuncion(); //Llamada al namespace Biblioteca raíz
?>
Muestra:
Hola |
La constante __NAMESPACE__ devuelve el nombre del espacio de nombres actual.
Es posible crear sub-espacios de nombres escribiendo:
Namespace Espacio1/subespacio1;
Las rutas para encontrar una función, clase o constante en un espacio de nombres son relativos si empieza...
Autoload
La función spl_autoload_register() permite evitar incluir manualmente todas las clases al principio de cada página PHP. Esta función toma como parámetro su función permitiendo incluir las clases necesarias.
Por ejemplo:
<?php
//Asignación de la función que gestiona el autoload
spl_autoload_register('miAutoload');
function miAutoload($className)
{
//Las clases se encuentran en la carpeta class
$path = '/class/';
//El nombre de los archivos debe corresponder al nombre de la clase
include $path.$className.'.php';
}
//Instanciación de la clase
$myClass = new MyClass();
?>
Ejercicios
1. Enunciados
Ejercicio 1 (fácil)
Cree las cinco clases del esquema teniendo en cuenta su herencia. Todos los métodos son públicos y los atributos son privados.
Ejercicio 2: continuación del ejercicio 1 (fácil)
Cree los accesos de todos los atributos. Cree un constructor en la clase vehículo que tome como argumento el color y el peso. Modifique el método circula() para que muestre "El vehículo circula". Modifique el método añadir_persona(peso_persona) para que cambie el peso del vehículo en función del peso de la persona que pasa como argumento.
Cree la página mostrar.php y un vehículo negro de 1500 kg. Haga que circule.
Añada una persona de 70 kg y muestre el nuevo peso del vehículo.
Ejercicio 3: continuación del ejercicio 2 (dificultad media)
Aplique el método repintar(color) para cambiar el color definido en la clase Vehículo. Ejecute el método poner_gasolina(litros) para cambiar el peso definido en la clase Vehículo. En este ejercicio, un litro equivale a un kilo.
Aplique los métodos añadir_cadenas_nieve() y quitar_cadenas_ nieve() modificando el atributo numero_cadenas_nieve.
Aplique el método añadir_remolque(longitud_remolque) modificando el atributo longitud.
En la página mostrar.php, cree un coche verde de 1400 kg. Añada dos personas de 65 kg cada una. Muestre su color y su nuevo peso.
Retome el coche en rojo y añada dos cadenas para la nieve.
Muestre su color y su número de cadenas para la nieve.
Cree un objeto Dos_ruedas negro de 120 kg. Añada una persona de 80 kg. Ponga 20 litros de gasolina.
Muestre el color y el peso de dos ruedas.
Cree un camión azul de 10000 kg y de 10 metros de longitud con 2 puertas. Añada un remolque de 5 metros y una persona de 80 kg.
Muestre su color, su peso, su longitud y su número de puertas.
Ejercicio 4: continuación del ejercicio 3 (dificultad media)
Convierta en abstractos la clase Vehículo y su método añadir_persona(peso_persona).
Defina el método añadir_persona(peso_persona) en la clase Dos_ruedas para que este método añada el peso de la persona, más 2 kg del casco del dos ruedas.
Defina el método añadir_persona(peso_persona) en la clase Cuatro_ruedas para hacer lo mismo que en la clase...