Diferencia entre revisiones de «Usuario:ManuelRomero/ProgramacionWeb/Objetos/introduccionBase»
(Página creada con « <div class=parrafo> {{PHP/ConceptosBasicosBase}} {{PHP/SintasixPHPOOPBase}} {{PHP/SobrecargaBase}} {{PHP/EstaticoBase}} {{PHP/HerenciaBase}} </div>») |
|||
| (Una revisión intermedia por el mismo usuario no mostrado) | |||
| Línea 1: | Línea 1: | ||
| + | {{Usuario:ManuelRomero/ProgramacionWeb/ObjetosBase/nav}} | ||
<div class=parrafo> | <div class=parrafo> | ||
{{PHP/ConceptosBasicosBase}} | {{PHP/ConceptosBasicosBase}} | ||
| − | |||
| − | |||
{{PHP/SintasixPHPOOPBase}} | {{PHP/SintasixPHPOOPBase}} | ||
| − | {{PHP/ | + | {{PHP/Sobrecarga}} |
| − | {{PHP/ | + | {{PHP/Estatico}} |
| − | {{PHP/ | + | {{PHP/Herencia}} |
</div> | </div> | ||
Última revisión de 05:13 6 nov 2025
Contenido
|}
Conceptos básicos de OOP
OOP Vs Programación estructurada
- En programación el paradigma imperativo está basado en funciones y datos.
- El paradigma orientado a objetos está basado en Objetos.
- Los objetos son el elemento básico y central de la programación orientada a objetos (OOP) o (POO).
- Podemos hablar de universo de discurso como el sistema que queremos automatizar por software
- Un Objeto es una entidad (concreta o abstracta) que presenta una actividad en un entorno concreto, en un determinado universo de discurso.
Objeto Cada elemento activo que identificamos dentro de un determinado universo de discurso.
|
Ejemplo
| |
|
Ejemplo
| |
|
| {{{1}}} |
- Puede parecer una forma más complicada de programar, pero es una manera de dividir la naturaleza del problema que estamos estudiando en unidades independientes que pueden interactuar entre ellas.
- Cada una de ellas va a tener una identidad propia asignando valores a sus atributos
- Cada una de ellas va a tener un comportamiento concreto que va a ser lo que sabe hacer para que los demás o el programa principal lo utilice
Elementos en la programación orientada a objetos
- De lo dicho anteriormente deducimos que tenemos dos elementos:
- Los atributos o características de la clase.
- Los métodos o comportamiento de la clase .
- Para crear objetos, previamente hay que definir su estructura.
- La definición de la estructura (atributos y métodos ) de componentes software se llama clase
|
- Elementos de la POO
|
- Elementos de la POO
|
OOP en PHP
- PHP no se diseñó originalmente como un lenguaje orientado a objetos, por lo que muchas de las características de este paradigma se han ido incorporando en las últimas versiones, especialmente a partir de la versión 5.3.
- PHP almacena el valor de un objeto como una referencia (dirección de memoria), no como una copia del valor.
- Esto implica que, si queremos pasar un objeto a través de la red, debemos serializarlo, para que viaje también el valor del mismo y no solo la dirección de memoria (que en destino carecería de sentido). Veremos este concepto más adelante.
- En PHP las clases tienen métodos y propiedades
- Propiedades: son los atributos o características de la clase.
- Métodos: representan el comportamiento de la clase.
- Definir una clase en PHP
class NombreClase { // propiedades // métodos }
- NombreClase es un identificador válido según la siguiente expresión regular:
^[a-zA-Z_][a-zA-Z0-9_]*$
- Se recomienda que el nombre de las clases comience con mayúscula.
- Los nombres de las clases no son sensibles a mayúsculas/minúsculas, aunque es buena práctica tratarlos como si lo fueran.
- Es muy recomendable guardar las clases en ficheros cuyo nombre coincida con el de la clase; esto permite la autocarga y forma parte de las buenas prácticas de programación.
Ejemplo
| |
|
Vamos a crear una clase llamada Fecha.
|
Declaramos la clase
- Utilizaremos siempre un fichero con el mismo nombre que la clase.
class Fecha { }
Los atributos
- Al declarar los atributos, aparece un concepto importante: la visibilidad o alcance del atributo, es decir, desde dónde puede verse o modificarse su valor.
- Encapsulación
- acceso a los componentes
- Al definir tanto las propiedades como los métodos, especificamos su nivel de acceso.
- Es una buena práctica no permitir acceso directo a los atributos, sino hacerlo a través de métodos específicos.
|
- Visibilidad
- Implementa el principio de encapsulación.
- Permite especificar desde qué ámbito se tiene acceso a un determinado elemento.
- Básicamente tenemos tres niveles:
- Desde la propia clase.
- Desde otras clases no relacionadas o desde el programa principal.
- Desde clases que extienden a otra (herencia).
- Por tanto, existen tres tipos de visibilidad:
- public
- private
- protected
- public: tipo de visibilidad asignada por defecto a los métodos (si no se especifica).
- En el caso de los atributos hay que declararla de forma explícita.
- El uso de la palabra reservada var está depreciado; antes se usaba para declarar atributos públicos.
- public
- Los elementos públicos pueden ser accesibles desde cualquier ámbito en el que exista una referencia al objeto.
- private
- Los elementos con este modificador solo son accesibles desde el interior de la propia clase (ni siquiera desde clases heredadas).
- En POO es habitual hacer todos los atributos privados y acceder a ellos mediante los métodos getter y setter.
A un elemento private de una clase tampoco se puede acceder desde clases derivadas, pero en PHP sí se puede acceder a los atributos privados de otro objeto de la misma clase:
class A { private $foo; public function compara(A $b) { // Se puede acceder directamente al atributo privado $foo de otro objeto de la misma clase if ($this->foo == $b->foo) { return true; } return false; } }
- protected
- Este tipo de visibilidad permite el acceso solo desde la propia clase y desde clases derivadas (herencia).
- Siguiendo con nuestro ejemplo de Fecha, declaramos los atributos
class Fecha { // Declaración de atributos private int $dia; private int $mes; private int $year; }
Los métodos
- Es la forma de especificar el comportamiento de la clase: lo que el objeto sabe hacer dentro del programa.
- Los métodos se definen usando la palabra reservada function.
- En PHP orientado a objetos existen métodos especiales llamados #métodos mágicos, que se ejecutan automáticamente cuando ocurre un evento concreto (por ejemplo, al crear o destruir un objeto).
- Métodos constructor y destructor
- En PHP, a diferencia de Java, no podemos usar un método con el mismo nombre que la clase (desde PHP 7.4 genera error).
- El constructor se define con el método mágico __construct() y se ejecuta automáticamente al instanciar un objeto.
- De forma similar, el método __destruct() se ejecuta al destruir un objeto.
- Ambos son opcionales y su invocación es transparente para el programador.
- En __construct() podemos pasar parámetros para inicializar los atributos del objeto.
- Ejemplo
- construyendo nuestra clase
class Fecha { private int $dia; private int $mes; private int $year; public function __construct(int $d, int $m, int $y) { $this->dia = $d; $this->mes = $m; $this->year = $y; } }
- En este código aparecen dos conceptos importantes
- La seudovariable $this
- El operador de indirección ->
$this y el operador de indirección ->
- Para acceder a los atributos o métodos de un objeto dentro de la clase usamos la seudovariable $this.
- $this hace referencia al objeto actual (la instancia sobre la que se está ejecutando el método).
- Si dentro de un método se omite $this->, PHP interpretará las variables como locales y no como atributos de la clase.
<?php class Persona { public $nombre; public $apellido; public function __construct(string $n, string $a) { // ERROR: asignación a variables locales, no a atributos $nombre = $n; $apellido = $a; } } $p = new Persona("María", "Ruiz"); echo "<h1>Nombre: $p->nombre</h1>"; echo "<h1>Apellido: $p->apellido</h1>";
public function __construct(string $n, string $a) { $this->nombre = $n; $this->apellido = $a; }
|
|
- El operador de indirección ->
- En PHP los objetos se manejan internamente como referencias en memoria.
- Para acceder a sus elementos se usa el operador ->, que “indirecciona” hacia el atributo o método deseado.
- Es la forma que tiene PHP de especificar a qué elemento concreto de un objeto se quiere acceder.
Declarando objetos: operador new
- Permite crear instancias (objetos) en memoria.
- Una clase describe la estructura común de un conjunto de objetos, su composición o plantilla.
- En un programa, crearemos objetos (instancias) a partir de las clases mediante el operador new.
- Una vez instanciado, tenemos una referencia al objeto y podemos usarlo.
- Recordemos que cada objeto ocupa su propio espacio en memoria (con sus propios atributos y métodos).
Ejemplo en el programa principal:
require "Fecha.php"; $f1 = new Fecha(10, 2, 1998);
Acceso al contenido del objeto
- Para acceder a un elemento de un objeto usamos los operadores -> o :: (este último para elementos estáticos).
- Operador de indirección ->
- Este operador permite acceder al contenido de un objeto a través de su referencia.
- Cuando se usa dentro de la clase, suele acompañarse de $this;
cuando se usa fuera, de una variable objeto.
class Clase1 { public $propiedad1; public function __construct($valor) { // No se usa $ delante del nombre de la propiedad $this->propiedad1 = $valor; } } $obj1 = new Clase1("verde"); $obj1->propiedad1 = "azul";
Sobrecarga
Es un concepto muy importante y básico en la programación orientada a objetos. La sobrecarga es una concreción del principio de polimorfismo.
|
- Sin embargo, este aspecto en PHP no es del todo intuitivo: no existe la sobrecarga como la entendemos en otros lenguajes.
- No obstante, disponemos de técnicas para poder simular la sobrecarga.
- En muchos casos resulta fundamental, especialmente al sobrecargar el constructor de una clase.
- Para simular la sobrecarga en PHP, aprovechamos que una variable que no tenga valor se considera del tipo null.
- Lo veremos con una serie de ejemplos para dejar claro este concepto.
- Tomamos como ejemplo una función:
Ejemplo
| |
function verTipoParametros($a = null, $b = null, $c = null){ echo "Primer parámetro: "; var_dump($a); echo "Segundo parámetro: "; var_dump($b); echo "Tercer parámetro: "; var_dump($c); }
|
- Ahora la invocamos de diferentes maneras y observamos el resultado:
Invocar sin parámetros reales
| |
echo "Invocando a <strong>verTipoParametros()</strong><hr />"; verTipoParametros();
|
Invocar con 1 parámetro real
| |
echo "Invocando a <strong>verTipoParametros(5)</strong><hr />"; verTipoParametros(5);
|
Invocar con 2 parámetros reales
| |
echo "Invocando a <strong>verTipoParametros(5,7)</strong><hr />"; verTipoParametros(5,7);
|
Invocar con 3 parámetros reales
| |
echo "Invocando a <strong>verTipoParametros('pedro',5,9)</strong><hr />"; verTipoParametros('pedro',5,9);
|
Invocar con 3 parámetros reales, uno de ellos un array
| |
echo "Invocando a <strong>verTipoParametros([1,4,'maría'], true, 'sonia')</strong><hr />"; verTipoParametros([1,4,'maría'], true, 'sonia');
|
Sobrecargando el constructor
- Usando esta forma de trabajar, vamos a sobrecargar el constructor de una clase.
- Tomemos como ejemplo una clase Racional. Un número racional es un objeto que tiene numerador y denominador.
- Queremos permitir crear el objeto de distintas formas:
$r1 = new Racional("8/5"); /* 8/5 */ $r2 = new Racional(5,4); /* 5/4 */ $r3 = new Racional(5); /* 5/1 */ $r4 = new Racional(); /* 1/1 */
- Necesitamos que el constructor pueda responder a todas las situaciones.
- Aplicando los conceptos vistos, solo tenemos que comprobar de qué tipo son los parámetros.
- Vemos que podemos tener 0, 1 o 2 parámetros.
Por lo tanto, el constructor tendrá dos parámetros opcionales.
public function __construct($num = 1, $den = 1) { ... }
- Especificamos el código de cómo se podría implementar:
class Racional { private $num; private $den; public function __construct($num = 1, $den = 1) { // opciones: // new Racional() => 1/1 // new Racional(5) => 5/1 // new Racional("5/2") => 5/2 // new Racional(5,2) => 5/2 // Otra situación: no se instancia correctamente if (is_string($num)) { $numero = explode("/", $num); $num = $numero[0]; $den = $numero[1]; } $this->num = $num; $this->den = $den; } /* Método para visualizar el objeto como cadena de caracteres */ public function __toString() { return ($this->num . "/" . $this->den); } // Métodos privados para asignar valores según la forma de invocación private function racionalNum($num) { $this->num = $num; $this->den = 1; } /** * @param string $num número racional del tipo "a/b" * Hay muchas formas de poder descomponer esa cadena en dos números. */ public function racionalCadena($num) { $partes = explode("/", $num); $this->num = (int) $partes[0]; $this->den = (int) $partes[1]; } public function racionalVacio() { $this->num = 1; $this->den = 1; } /* En este caso, si los valores son incorrectos, asigno el racional 1/1 */ public function racionalNumDen($num, $den) { if (is_numeric($num) && is_numeric($den)) { $this->num = $num; $this->den = $den; } else { $this->num = 1; $this->den = 1; } } }
- Esta sería una posibilidad, pero podemos crear un código más compacto.
En la siguiente versión inicializamos los parámetros con un valor por defecto (1). Posteriormente, si el primer parámetro es una cadena, se divide en numerador y denominador. Hecho esto, solo queda asignar los valores a los atributos.
public function __construct($num = 1, $den = 1){ if (is_string($num)){ $numero = explode("/", $num); $num = $numero[0]; $den = $numero[1]; } $this->num = $num; $this->den = $den; }
- Probamos este constructor con el siguiente código:
$a = new Racional(); $b = new Racional(5); $c = new Racional(5,6); $d = new Racional("6/6"); echo "Valor del racional \$a = $a <br />"; echo "Valor del racional \$b = $b <br />"; echo "Valor del racional \$c = $c <br />"; echo "Valor del racional \$d = $d <br />";
- Mostrando los siguientes resultados:
Valor del racional $a = 1/1 Valor del racional $b = 5/1 Valor del racional $c = 5/6 Valor del racional $d = 6/6
Sobrecarga con __call(...)
- Otra forma de lograr un comportamiento similar es usando el método mágico __call($funcion, $parametros).
- Queremos usar un método llamado asigna() que nos permita cambiar el valor de un racional.
- La forma de aportar el nuevo valor será la misma que la de construir el objeto.
$r1 = new Racional(); // construye el objeto 1/1 $r1->asigna("6/4"); // ahora el objeto vale 6/4 $r1->asigna(); // ahora el objeto vale 1/1 $r1->asigna(8); // ahora el objeto vale 8/1 $r1->asigna(124, 6); // ahora el objeto vale 124/6
- La forma de proceder será usando los métodos privados creados anteriormente.
- El siguiente código implementa la solución:
public function __call($metodo, $argumentos) { if ($metodo == "asigna") { switch (count($argumentos)) { case 0: $this->racionalVacio(); break; case 2: $this->racionalNumDen($argumentos[0], $argumentos[1]); break; case 1: if (is_int($argumentos[0])) { $this->racionalNum($argumentos[0]); } else { $this->racionalCadena($argumentos[0]); } break; } } }
Métodos static vs no static
- En PHP, la idea de static es igual que en cualquier lenguaje de programación orientado a objetos.
- Cuando un elemento (atributo o método) es estático, ese elemento es compartido por todos los objetos de la clase y persiste en memoria con su contenido mientras exista al menos un objeto de esa clase.
- Como no pertenece a cada objeto individual, sino a la clase en general, también se les llama atributos o métodos de clase.
- Para acceder a un elemento estático, necesitamos nombrar la clase (no el objeto). En PHP podemos hacerlo usando el operador self o el propio nombre de la clase junto con el operador de resolución de ámbito ::.
|
- Es muy común tener un atributo static que cuente cuántos objetos existen de una clase.
- En la siguiente imagen se ilustra cómo al crear varios objetos, cada uno se instancia por separado en memoria:
- Cuando añadimos un elemento estático, solo habrá una copia en memoria, compartida por todos los objetos de la clase:
- Vemos el código y cómo se accede al elemento estático:
<?php class Racional { static public $cuenta_racionales = 0; private $num; // Numerador private $den; // Denominador public function __construct($num, $den) { self::$cuenta_racionales++; $this->num = $num; $this->den = $den; } } ?>
- El uso:
<?php require_once "Racional.php"; $r1 = new Racional(5,4); $r2 = new Racional(5,4); $r3 = new Racional(5,4); echo "<h1>Ahora tenemos " . Racional::$cuenta_racionales . " objetos Racional</h1>"; $r4 = new Racional(5,4); $r5 = new Racional(5,4); echo "<h1>Ahora tenemos " . Racional::$cuenta_racionales . " objetos Racional</h1>"; $r6 = new Racional(5,4); $r7 = new Racional(5,4); // Observa (y esto es propio de PHP) que puedo acceder a un elemento estático // tanto a través del nombre de la clase como desde un objeto echo "<h2>Podemos acceder con los objetos:</h2>"; echo "<h3>Según r1: " . $r1::$cuenta_racionales . "</h3>"; echo "<h3>Según r2: " . $r2::$cuenta_racionales . "</h3>"; echo "<h3>Según r3: " . $r3::$cuenta_racionales . "</h3>"; echo "<h3>Según la clase: " . Racional::$cuenta_racionales . "</h3>"; ?>
- La salida:
---
- Vamos a implementar las operaciones con números racionales.
Antes, recordemos las operaciones básicas:
- Sumar
- Restar
- Multiplicar
- Dividir
- Si la operación la implementamos como un método no estático, lo que estaremos haciendo es sumar al objeto actual otro objeto Racional que pasamos como argumento.
Podemos modificar el objeto actual o devolver un nuevo objeto (lo más correcto).
/** * Suma al racional actual el racional recibido como parámetro. * @param Racional $n1 * @return Racional */ public function sumar(Racional $n1) { $den = $this->den * $n1->getDen(); $num = $this->num * $n1->getDen() + $this->den * $n1->getNum(); return new Racional($num, $den); }
- Para usarlo:
$r1 = new Racional(7,6); $r2 = new Racional(9,4); $r3 = $r1->sumar($r2); echo "$r1 + $r2 = $r3";
- La salida:
---
- Si la operación la realizamos como un método estático, estaremos sumando dos objetos Racional y devolviendo un nuevo objeto como resultado:
static public function sum_static(Racional $r1, Racional $r2){ $n = $r1->getNum() * $r2->getDen() + $r1->getDen() * $r2->getNum(); $d = $r1->getDen() * $r2->getDen(); return new Racional($n, $d); }
- Para usarlo:
$r1 = new Racional(7,6); $r2 = new Racional(9,4); $r3 = Racional::sum_static($r1, $r2); echo "$r1 + $r2 = $r3";
- La salida:
Herencia
- La herencia es un mecanismo de programación que permite crear una jerarquía en los componentes software, que se van especializando.
- Es un principio de abstracción mediante el cual podemos crear una jerarquía de clases, con una raíz que contiene los elementos comunes y nodos que representan clases especializadas.
- La idea es definir una clase con ciertas características comunes (atributos, métodos). Posteriormente, crearemos otras clases a partir de la ya existente, heredando implícitamente los atributos y métodos como parte de su estructura o composición.
- Es una característica muy natural. Por ejemplo:
- Personas → (Médicos, Bailarines) - Vehículos → (Terrestres → Coche, Moto) / (Acuáticos → Barco, Lancha)
- La herencia es una forma de obtener características comunes por separado y luego especializarlas, evitando redundancias.
- Facilita la reutilización y la adaptación del código.
- La herencia implica declarar jerarquías de clases
- En la raíz de la jerarquía establecemos la parte o estructura común a todas las clases, y posteriormente vamos especializando las diferencias de cada una.
|
- Para establecer una jerarquía, usamos la palabra reservada extends en las clases que heredan.
- Vamos a verlo con un ejemplo:
- Primero establecemos la clase Persona:
<?php class Persona { protected $nombre; protected $direccion; protected $edad; protected $frase; public function __construct(string $n, string $d, int $e){ $this->nombre = $n; $this->direccion = $d; $this->edad = $e; } public function establecer_frase(string $frase){ $this->frase = $frase; } public function hablar(){ echo $this->frase; } } ?>
- Ahora establecemos la clase que hereda de Persona:
|
<?php class Sanitario extends Persona { protected $centroSalud; protected $yearTitulacion; public function __construct(string $n, string $d, int $e, string $centro, int $year) { parent::__construct($n, $d, $e); $this->centroSalud = $centro; $this->yearTitulacion = $year; } public function mostrarInfo(){ echo "$this->nombre trabaja en $this->centroSalud y obtuvo su título en $this->yearTitulacion."; } } ?>
---
- Vemos dos ejemplos para explicar de forma práctica este concepto.
Herencia: gestión de personal en un ambulatorio
| |||
Las propiedades (atributos) y métodos de cada clase se representan en los siguientes diagramas:
El diagrama podría quedar así:
http://manuel.infenlaces.com/dwes/ejercicios/T6_Ambulatorio/
|
Clases Abstractas
- Cuando realizamos jerarquías, muchas veces encontramos métodos comunes a varias clases.
Esto implicaría que ese método debería pertenecer a una superclase (o clase padre), de la que luego se heredará.
- Pero puede ocurrir que, aunque el concepto del método sea común a todas las clases, la forma de implementarlo sea diferente en cada una.
Clase abstracta
| |
|
- En este caso, la forma correcta de proceder es especificar el método en la clase superior y dejar su implementación a las clases derivadas.
- El método especificado en la clase superior será un método sin código, conocido como método abstracto, y la clase donde se define pasa a ser una clase abstracta.
|
|
- Vamos a plantear un ejemplo práctico:
















