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.
Definición
Objeto Cada elemento activo que identificamos dentro de un determinado universo de discurso.
- Serán nuestros componentes software para ensamblar nuestros programas
|
|
Ejemplo
|
|
- En un banco hay cuentas bancarias (objeto)
- Las cuentas bancarias se identifican con un número y un titular (nombre, apellido y dni) atributos
- Las cuentas se pueden dar de alta, de baja, hacer extracciones e ingresos y transferencias... métodos
|
|
Ejemplo
|
|
- En la gestión de una empresa, a nivel de información tenemos
-
- Empleados
- Nóminas
- Base de datos
- Proveedores
- Facturas
- Pedidos
|
|
Compartiva con la aplicación de master mind
|
|
|
- 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
Clase
- La descripción y especificación de componentes software para su posterior uso en los programas
- Una clase es la estructura de un tipo concreto de objetos.
- Los objetos son elementos concretos en mi sistema. Instancias de la clase en memoria para ser usadas por un programa
|
- Elementos de la POO
Atributo
- Son las características o datos de un objeto.
- Sus valores nos da el estado de un objeto en un momento dado.
- Normalmente al instanciar un objeto en memoria lo primero que hacemos es dar valores a sus atributos
- Es recomendado que los atributos estén encapsulados solo al objeto (privados)
|
- Elementos de la POO
Métodos
- Especifican el comportamiento de los objetos.
- Permiten modificar y conocer el estado de un objetos (métodos getter and setter).
- Permiten que un objeto haga cosas en el sistema (comunicación entre objetos) .
- Los métodos son las acciones que el objeto sabe hacer, servicios que ofrece
- También son las acciones internas para facilitar las acciones al objeto
|
Sobrecarga
Un concepto muy importante y básico en la programación orientada a objetos.
La sobrecarga es una concreción del principio de polimorfismo
polimorfismo
- Podemos tener varios métodos con el mimos nombre, pero diferente número de parámetros o con parámetros de diferente tipo
- El tiempo de ejecución se ejecutará uno u otro en función de los parámetros reales que pasemos en la invocación del método
|
- Sin embargo este aspecto en php no es del todo intuitivo. No existe la sobrecarga como la entendemos en otros lenguajes.
- No obstante tenemos técnicas para poder simular la sobrecarga.
- Muchas veces es fundamental. Especialmente importante a la hora de sobrecargar el constructor de la clase.
- Para simular la sobrecarga en php, jugamos con el concepto de que una variable que no tenga valor se considera de tipo null.
- Lo vemos 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);
}
</nowiki>
|
- Ahora la invocamos de diferente manera y vemos el resultado:
|
Invocar sin parámetros reales
|
|
echo "Invocando a <strong>verTipoParametros ()</strong><hr / />";
verTipoParametros ();
}
- A pesar de que tiene tres parámetros, la invocamos sin parámetros
- El resultado será que cada parámetro al ejecutar la función será de tipo null con valor null (es un tipo válido en php):
|
|
Invocar con 1 parámetro real
|
|
...
echo "Invocando a <strong>verTipoParametros (5)</strong><hr />";
verTipoParametros (5);
...
- En esta caso invocamos con un solo parámetro de tipo entero
|
|
Invocar con 2 parámetros reales
|
|
...
echo "Invocando a <strong>verTipoParametros (5,7)</strong><hr />";
verTipoParametros (5,7);
...
- En esta caso invocamos con dos parámetros de tipo entero
- Al igual que en caso anterior los parámetros en la función serían 3, dos de ellos con valor de tipo entero, y el tercero con valor y tipo null
- Podemos ver el resultado
|
|
Invocar con 3 parámetros reales
|
|
...
echo "Invocando a <strong>
verTipoParametros ('pedro',5,9)
</strong><hr />";
verTipoParametros ('pedro',5,9);
...
- Ahora pasamos tres parámetros, como vemos el primero de tipo string y los otros dos enteros
- Podemos ver el resultado
|
|
Invocar con 3 parámetro 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');
...
- Ahora igualmente pasamos tres parámetros, pero uno de ellos es un array
- Podemos ver el resultado
|
Sobrecargando el constructor
- Usando esta forma de trabajar vamos a sobre cargar el constructor de una clase
- Tomamos una clase de tipo Racional. Un número Racional es un objeto que tendrá numerador y denominador
- Ahora a la hora de construir el objeto planteamos la posibilidad de poder instanciar de la siguiente manera:
$r1 = new Racional ("8/5");/* 8/5 */
$r2 = new Racional (5,4); /* 5/6 */
$r3 = new Racional (5); /* 5/1 */
$r4 = new Racional (); /* 1/1 */
- Aquí vemos claramente que necesitamos sobrecargar el constructor para que pueda responder a todas las situaciones
- Aplicando los conceptos vistos antes, lo único que tenemos que hacer en el constructor es ir viendo de qué tipo' son los parámetros.
Tip: Recordar que null también es un tipo
- Vemos que podemos tener 0, 1 o 2 parámetros
- Por lo tanto la función constructora tendrá que tener 2 parámetros
public function __construct($num=1, $den=1) {
....
}
- Especificamos el código de cómo se podría hacer
class Racional {
private $num;
private $den;
public function __construct($num=1, $den=1) {
//opciones new Racional () =>1/1
//opciones new Racional (5) =>5/1
//opciones new Racional ("5/2") =>5/2
////opciones new Racional (5,2) =>5/2
//Otra sitiación no se instancia
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);
}
//A continuación los métodos privados para asignar valores
private function racionalNum($num) {
$this->num = $num;
$this->den = 1;
}
/**
*
* @param string $num numero racional del tipo "a/b"
* hay muchas forma de poder descomponer ese array en dos números
*/
public function racionalCadena($num) {
$this->num = (int) $num;
$this->den = substr($num, strpos($num, "/") + 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 un posibilidad, pero debemos de intentar crear un código más compacto y realizar una estratelgia más compacta. El ejemplo anterior lo podríamos compactar de la siguiente manera.
En ella vamos a inicializar los parámetros con un valor que quiero que tengan si no se aporta valor 1
Posteriormente en el código establezco la situación en el caso de que el primer parámetro sea string y asigno los valores a numerador y denominador.
Hecho esto, ya hemos considerado todas las opciones, ahora ya solo queda inicializar los atributos con estos valores.
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 poder hacer lo mismo es usando el método mágico __call($funcion, $parametros).
Tip: El método mágico __call(..) es ejecutado cuando invocamos a un método que no existe en la clase
- Ahora queremos usar un método llamado asigna que nos permita cambiar el valor de un racional. La forma de aportar el nuevo valor, queremos que sea la misma que la forma 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.
- Cuando un elementos (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 haya un objeto de esa clase.
- Como no es un elemento de cada objeto, sino de todos los objetos de una clase, también se le suelen llamar atributos o métodos de la clase.
- Como no es de la clase para acceder a él necesitamos nombrar la clase. En php se puede hacer suando el operador self o incluso el propio nombre de la clase
Usar constantes y elementos estáticos
- Como ejemplo vamos a implementar una Factura y utilizarlas con los siguientes requisitos
- La factura tendrá una constante llamada IVA
- Tendremos un atributo estáticos que especificará el número de facturas que tengo
- Los atributos de factura serán importe_bruto y fecha
- Tendrá un método generar factura que nos visualizará una factura con los siguientes datos:
- Factura de XXXX (El nombre lo recibirá como argumento)
- Fecha (La fecha de la factura que es un atributo)
- Importe (El importe base que es un atrubuto)
- IVA aplicado (El valor de la constante iva)
- Total bruto (El importe base más el iva)
- En el index crearemos 5 facturas, visualizaremos el número de facturas, elimniaremos dos de ellas y visualizaremos el número de facturas e imprimieremos las 2 facturas que nos queden
Posible solucion |
---|
<?php
//Factura.php
class Factura
{
// Constante IVA
const IVA = 0.21;
// Atributo estático para contar el número de facturas
private static $numeroFacturas = 0;
// Atributos de la factura
private $importe_bruto;
private $fecha;
// Constructor
public function __construct($importe_bruto, $fecha)
{
$this->importe_bruto = $importe_bruto;
$this->fecha = $fecha;
self::$numeroFacturas++; // Incrementa el número de facturas al crear una nueva instancia
}
// Destructor
public function __destruct()
{
self::$numeroFacturas--; // Decrementa el número de facturas al eliminar una instancia
}
// Método para generar una factura
public function generarFactura($nombreCliente)
{
$ivaAplicado = $this->importe_bruto * self::IVA;
$totalBruto = $this->importe_bruto + $ivaAplicado;
echo "Factura de $nombreCliente\n";
echo "Fecha: {$this->fecha}\n";
echo "Importe base: {$this->importe_bruto} €\n";
echo "IVA aplicado (" . (self::IVA * 100) . "%): {$ivaAplicado} €\n";
echo "Total bruto: {$totalBruto} €\n";
echo "-----------------------------\n";
}
// Método estático para obtener el número de facturas actuales
public static function obtenerNumeroFacturas()
{
return self::$numeroFacturas;
}
}
//index.php
// Código en el índice
// Crear 5 facturas
$factura1 = new Factura(100, '2024-11-18');
$factura2 = new Factura(200, '2024-11-18');
$factura3 = new Factura(150, '2024-11-18');
$factura4 = new Factura(300, '2024-11-18');
$factura5 = new Factura(250, '2024-11-18');
// Visualizar el número de facturas
echo "Número de facturas actuales: " . Factura::obtenerNumeroFacturas() . "\n\n";
// Eliminar dos facturas
unset($factura4);
unset($factura5);
// Visualizar el número de facturas tras eliminar dos
echo "Número de facturas tras eliminar: " . Factura::obtenerNumeroFacturas() . "\n\n";
// Imprimir las facturas restantes
$factura1->generarFactura("Cliente 1");
$factura2->generarFactura("Cliente 2");
|
|
- Es un ejemplo típico tener un atributo static que cuente cuantos objetos hay de una clase.
- Miramos la siguiente imagen que trata de ilustrar como al crear objetos se instancian en memoria
- Cuando añadimos un elemento estático, solo habrá una copia en memoria y es compartida por todos los objetos de la clase
- Vemos el código y como se accede al elemento
<?php
class Racional {
static public $cuenta_racionales=0;
//atributos
private $num; //el numerador
private $den; //el denominador
//......
public function __construct($num, $den) {
self::$cuenta_racionales++;
.......
<?php
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
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 racionales</h1>";
$r4 = new Racional(5,4);
$r5 = new Racional(5,4);
echo "<h1>Ahora tenemos" .Racional::$cuenta_racionales."</h1>";
$r6 = new Racional(5,4);
$r7 = new Racional(5,4);
//Observa (y esto es de php ) que puedo acceder a un elemento estático
//A traves del nombre del objeto
echo "<h1>Ahora tenemos" .Racional::$cuenta_racionales."</h1>";
echo "<h1>Podemos acceder con los objtetos </h2>";
echo "<h1>Según r1 tenemos " .$r1::$cuenta_racionales."</h1>";
echo "<h1>Según r2 tenemos " .$r2::$cuenta_racionales."</h1>";
echo "<h1>Según r3 tenemos " .$r3::$cuenta_racionales."</h1>";
echo "<h1>Según r4 tenemos " .$r4::$cuenta_racionales."</h1>";
echo "<h1>Según La clase tenemos " .Racional::$cuenta_racionales."</h1>";
?>
- Vamos a implementar las operaciones en un Racional.
Primero como siempre conviene recordar cómo se hacen
- Sumar
- Restar
- Multiplicar
- Dividir
- Si la operación la implemento como un método no estático lo que estaría haciendo es sumar al objeto acutual, otro objeto Racional que pasamos como argumento. Puedo modificar el objeto acutal como resultado de la suma, o devolver otro objeto Racional que sería lo mas correcto
/**
*
* @param Racional $n1
* @return \Racional
* @description suma al racional actual, el racional que recibe como parámetro
*/
public function sumar(Racional $n1) {
$den = ($this->den * $n1->getDen());
$num = ($this->den * $n1->getNum() + $this->num * $n1->getDen());
return new Racional($num, $den);
}
....
$r1 = new Racional(7,6);
$r2 = new Racional(9,4);
$r3 = $r1->sumar($r2)
echo "$r1 + $r2 = $r3);
- Si la operación la realizo como un método estático lo que estaría haciendo es sumar dos objetos Racionales , devolviendo un nuevo objeto de tipo Racional Vamos
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);
....
$r1 = new Racional(7,6);
$r2 = new Racional(9,4);
$r3 = Racional::sum_static($r1,$r2);
echo "$r1 + $r2 = $r3);
Herencia
- La herencia es un mecanismo de programación que me permite crear una jerarquía en los componentes software, que se pueden ir especializando
- Es principio de abstracción por el cual podemos crear una jerarquía de clases, donde tenemos la raíz con los elementos comunes y en los nodos vamos especializando clases.
- La idea es definir una clase con ciertas características comunes (atributos, métodos)
Posteriormente crearemos otras clases a partir de la ya existente, quedando implícitamente los atributos y métodos como también parte de su estructura o composición.
- Es una característica muy natural (p.e Personas (médicos y bailarines) vehículos (Terrestres (coche, moto) Acuáticos (barco, lancha))
- Es una forma de obtener características comunes por separado y luego especializar evitando redundancias
- Facilita la reusabilidad y adaptación
- La herenica implica declarar jerarquía de clases
- En la raíz de la misma, establecemos la parte o estructura común a dichas clases , y posteriormente vamos especializando las diferencias de cada clase
- Todos los atributos y métodos de un supertipo o clase en nivel superior que sean públicos o protegidos, son heredados (son parte de ella) a todas las clases de nivel inferior.
|
- Para establecer una jerarquía, usamos la palabra reservada extends en las clases que hereden.
- 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, string $e){
}
public function establecer_frase ($frase){
}
public function hablar ($frase){
}
}
- Ahora establecemos la clase que hereda de persona
Principios del uso de la herencia
- Todos los atributos y métodos públicos y protegidos del supertipo Persona son también de Sanitario
- En un momento dado, podemos invocar a los métodos del supertipo usando el operador parent seguido del operador de resolución de ámbito ::
|
<?php
class Sanitario extends Persona
{
protected $centroSalud;
protected $yearTitulacion;
public function __construct(string $centro, string $year){
parent::__construct();
....
}
- Vemos dos ejemplos para explicar de forma empírica este concepto
|
Herencia: gestión personal ambulatorio
|
|
- Se pide gestionar un ambulatorio.
- Para ello vamos a hacer sólo el diagrama de clases y su implementación.
- Lo hacemos a nivel básico (sin entrar en detalles).
- Tras realizar un análisis se determina que se pretende gestionar:
- los datos de los empleados y anotar las acciones básicas que realizan.
- Encontramos los siguientes elementos que especificamos como clases
- Conserjes
- Enfermeras
- Médicas
Las propiedades (atributos) y métodos de cada clase se especifican
en los siguientes diagramas de clases
- Claramente vemos que todos ellos tienen varios elementos en común.
- Esto nos permite crear una clases genérica que por ejemplo podemos llamar personalAmbulatorio
- Posteriormente creamos una especialización de personalAmbulatorio con los elementos particulares
- El digrama podría quedar
- Ver la aplicación ejecutándose
http://manuel.infenlaces.com/dwes/ejercicios/T6_Ambulatorio/
Posible index de uso |
---|
<?php
spl_autoload_register(function ($nombre_clase) {
include $nombre_clase . '.php';
});
$medico1 = new Medica("María", "Martínez", "Casa de María", 29, "Cardiólogía");
$medico2 = new Medica("Luis", "Pérez", "Casa de Luis", 38, "Pediatría");
$medico3 = new Medica("Nieves", "Ruiz", "Casa de Nieves", 44, "Dermatología");
$conserje = new Conserje("Soledad", "Viruela", "Casa de Soledad", 58, "Mostrador Entrada");
$enfermera = new Enfermera("Javier", "Moreno", "Casa de Javier", "general", 1990);
$enfermera2 = new Enfermera("Luis", "Perez", "Casa de Javier", "general", 1990);
$conserje->avisoEnfermera($enfermera, "Realizar cura en brazo Señor Martínez NSS 50/2155441/35");
$enfermera->avisoMedico($medico3, "Paciente con toss y fiebre");
$enfermera2->avisoMedico($medico2, "Paciente con toss y fiebre");
$enfermera2->avisoMedico($medico2, "Paciente con Vómitos");
$enfermera2->avisoMedico($medico3, "Paciente con pie torcido, impresión de rotura");
$conserje->avisoMedico($medico1, "Visitar en casa con mucha fiebre", "Visita");
$conserje->avisoMedico($medico1, "Problemas para desplazarse ", "Visita");
$conserje->avisoMedico($medico2, "Mareos y vétigos", "Visita");
$conserje->avisoMedico($medico2, "Persona mayor con poca mobilidad", "Visita");
$conserje->avisoMedico($medico3, "Niño pequeño con fiebre", "Visita");
$conserje->avisoMedico($medico1, "Caída en el parque", "Consulta");
?>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
</style>
<link rel="stylesheet" href="./stilo.css" type="text/css"/>
</head>
<body>
<?php
echo "$medico3";
echo "$medico1";
echo "$medico2";
// put your code here
?>
</body>
</html>
|
|
Clases Abstractas
- Cuando realizamos jerarquías muchas veces encontramos métodos comunes a varias clases. Esto implicaría que ese método sería un método de una superclase o clase padre de la que luego se heredará
- Pero puede ocurrir que aunque el concepto del método es común a todas las clases,la forma de implementarla es particular en cada una de ellas.
- En este caso, la forma correcta de proceder, es especificar el método el la clase superior, e implementar el código en cada una de las clases que derivo.
- El método especificado en la clase superior sería un método sin código, conocido como un método abstracto, y la clase dónde se especifica pasa a ser abstracta
Clase Abstracta
Es aquella clase que tiene un método o más abstracto
|
Método Abstracto
- Es un método que no tiene código asociado
- El código se implementará en las clases derivadas
|
- Nunca podremos instanciar un objeto de una clase abstracta
- Esto es normal, ya que ese objeto no tendría instrucciones para su/s método/s abstracto/s
|
- Vamos a platear un ejemplo
|
App de Geometría
|
|
- Gestionar figuras geométricas de tipo triángulo, cuadrado y Rectángulo.
- De ellas queremos conocer:
- el número de lados.
- calcular el área.
- dibujar el polígono (Esto insertaremos un pequeño código de javascript de uso básico del canvas
- conocer el número de lados del polígono.
- Realizamos los siguientes clases
- Clase Polígono
Descripción de la Clase Polígono
- Clase Rectángulo
Descripción de la Clase Rectángulo
- Clase Cuadrado
Descripción de la Clase Cuadrado
- Clase Triangulo
Descripción de la Clase Triángulo
- Uso de esta aplicación
- Un posible código index.php que lo único que hace es crear objetos y visulizar su área y dibujarlos
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<?php
spl_autoload_register(function ($clase){
require "$clase.php";
});
$triangulo = new Triangulo(200,300);
$cuadrado = new Cuadrado(200);
$rectangulo = new Rectangulo(200,100);
echo "<h4>Triángulo, ". Poligono::lados($triangulo)." y área ".
$triangulo->area()." px<sup>2</sup></h4>";
echo "<h4>Cuadrado, ". Poligono::lados($cuadrado)." y área ".
$cuadrado->area()." px<sup>2</sup></h4>";
echo "<h4>Rectángulo, ". Poligono::lados($rectangulo)." y área ".
$rectangulo->area()." px<sup>2</sup></h4>";
echo $triangulo->dibuja();
echo $cuadrado->dibuja();
echo $rectangulo->dibuja();
?>
</body>
</html>
- Resultado de su ejecución
|
V
|