Diferencia entre revisiones de «Usuario:ManuelRomero/ProgramacionWeb/Objetos/introduccion»

De WikiEducator
Saltar a: navegación, buscar
Línea 22: Línea 22:
 
{{PHP/ConceptosBasicos}}
 
{{PHP/ConceptosBasicos}}
  
{{PHP/SintasixPHPOOP}}
+
{{PHP/Sobrecarga}}
 +
{{PHP/Estatico}}
 +
{{PHP/Herencia}}
 +
 
  
 
</div>
 
</div>
  
 
|}
 
|}

Revisión de 10:40 1 ene 2020



Práctica | 



DWES Tema6 .png

Conceptos básicos de la Programación Orientada a Objetos (OOP o POO)

Conceptos básicos de la Programación Orientada a Objetos (OOP o POO)

OOP vs. Programación estructurada

  • La programación orientada a objetos (POO) surge como una evolución de la programación estructurada.
  • Busca modelar los problemas del mundo real utilizando objetos que combinan datos y comportamientos.


  • En programación, el paradigma imperativo se basa en funciones y datos.
  • El paradigma orientado a objetos se basa en objetos.
  • Los objetos son el elemento básico y central de la programación orientada a objetos (OOP o POO).

(Comment.gif: Cada objeto en el programa representa algo del mundo real: una persona, una cuenta bancaria, un pedido, una factura, etc.)



  • Podemos hablar de un universo de discurso, entendido como el sistema que queremos automatizar mediante software.
  • Un objeto es una entidad (concreta o abstracta) que desarrolla una actividad en un entorno determinado, dentro de un universo de discurso específico.



Icon define.gif
Definición

Objeto: cada elemento activo que identificamos dentro de un determinado universo de discurso.

Serán nuestros componentes software para construir y ensamblar nuestros programas.




Icon casestudy.gif
Ejemplo
En un banco hay cuentas bancarias (objeto)
Las cuentas bancarias se identifican con un número y un titular (nombre, apellidos y DNI) → atributos.
Las cuentas pueden darse de alta o de baja, hacer extracciones, ingresos y transferencias → métodos.




Icon casestudy.gif
Ejemplo
En la gestión de una empresa, a nivel de información tenemos
  • Empleados
  • Nóminas
  • Bases de datos
  • Proveedores
  • Facturas
  • Pedidos

UNIVERSO DISCUROS OBJETOS.png




Icon casestudy.gif
Comparativa con la aplicación del juego Mastermind
Comparativa oo estructurado.png



  • Puede parecer una forma más compleja de programar, pero en realidad es una manera de dividir la naturaleza del problema en unidades independientes que pueden interactuar entre sí.
  • Cada una de ellas tendrá una identidad propia, definida por los valores de sus atributos.
  • Cada una tendrá también un comportamiento concreto, es decir, lo que sabe hacer y que el resto del programa o de los objetos podrá utilizar.


Elementos en la Programación Orientada a Objetos

  • De lo dicho anteriormente deducimos que en la POO tenemos dos elementos fundamentales:
  1. Los atributos o características de la clase.
  2. Los métodos o comportamientos de la clase.
  • Para crear objetos, previamente hay que definir su estructura.
  • La definición de la estructura (atributos y métodos) de los componentes software se denomina clase.



Icon define.gif
Clase
La descripción y especificación de los componentes software para su posterior uso en los programas.
  • Una clase es la estructura o plantilla que define un tipo concreto de objetos.
  • Los objetos son elementos concretos de mi sistema: instancias de esa clase, cargadas en memoria para ser utilizadas por un programa.


Elementos de la POO



Icon define.gif
Atributo
  • Son las características o datos de un objeto.
  • Sus valores determinan el estado del objeto en un momento dado.
  • Normalmente, al instanciar un objeto en memoria, lo primero que hacemos es asignar valores a sus atributos.
  • Es recomendable que los atributos estén encapsulados, es decir, que sólo sean accesibles dentro del propio objeto (privados).




Icon define.gif
Método
  • Especifican el comportamiento de los objetos.
  • Permiten modificar o conocer el estado de un objeto mediante métodos getter y setter.
  • Permiten que un objeto realice acciones dentro del sistema y se comunique con otros objetos.
  • Los métodos representan las acciones que el objeto sabe hacer, los servicios que ofrece al sistema.
  • También pueden incluir acciones internas que facilitan el funcionamiento del propio objeto.
  • En PHP, los métodos se definen dentro de la clase y se invocan mediante el operador ->.


Clase.png

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.



Icon define.gif
Polimorfismo
  • Podemos tener varios métodos con el mismo nombre, pero con diferente número de parámetros o con parámetros de distinto tipo.
  • En 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, 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:



Icon casestudy.gif
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:



Icon casestudy.gif
Invocar sin parámetros reales
echo "Invocando a <strong>verTipoParametros()</strong><hr />";
verTipoParametros();
  • A pesar de que tiene tres parámetros, la invocamos sin argumentos.
  • 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).

FuncionSinParametrosReales.png




Icon casestudy.gif
Invocar con 1 parámetro real
echo "Invocando a <strong>verTipoParametros(5)</strong><hr />";
verTipoParametros(5);
  • En este caso invocamos con un solo parámetro de tipo entero.

Funcion1ParametrosReal.png




Icon casestudy.gif
Invocar con 2 parámetros reales
echo "Invocando a <strong>verTipoParametros(5,7)</strong><hr />";
verTipoParametros(5,7);
  • En este caso invocamos con dos parámetros de tipo entero.
  • Los parámetros dentro de la función serán tres: dos con valor entero y el tercero con valor y tipo null.

Funcion2ParametrosReal.png




Icon casestudy.gif
Invocar con 3 parámetros reales
echo "Invocando a <strong>verTipoParametros('pedro',5,9)</strong><hr />";
verTipoParametros('pedro',5,9);
  • En este caso pasamos tres parámetros: el primero de tipo string y los otros dos de tipo entero.

Funcion3ParametrosReal.png




Icon casestudy.gif
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');
  • Ahora igualmente pasamos tres parámetros, pero uno de ellos es un array.

FuncionParametroRealArray.png



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.

ClaseRacional.png

  • 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.
Icon present.gif
Tip: Recordar que null también es un tipo.


  • 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).
Icon present.gif
Tip: El método mágico __call(...) se ejecuta cuando invocamos un método que no existe en la clase.


  • 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 ::.



Icon activity.jpg
Usar constantes y elementos estáticos
Como ejemplo, vamos a implementar una clase Factura con los siguientes requisitos
  • La factura tendrá una constante llamada IVA.
  • Tendremos un atributo estático que especificará el número de facturas creadas.
  • Los atributos de cada factura serán importe_bruto y fecha.
  • Tendrá un método generarFactura() que mostrará:
  1. Factura de XXXX (el nombre se recibirá como argumento).
  2. Fecha (atributo).
  3. Importe base (atributo).
  4. IVA aplicado (constante de la clase).
  5. Total bruto (importe base más el IVA).
  • En el archivo index.php crearemos 5 facturas, visualizaremos el número de facturas, eliminaremos dos de ellas y volveremos a visualizar el número de facturas. Finalmente imprimiremos las dos facturas restantes.




  • 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:
Estatico1.png
  • Cuando añadimos un elemento estático, solo habrá una copia en memoria, compartida por todos los objetos de la clase:
Estatico2.png
  • 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:

AppEstatica1.png

---

  • Vamos a implementar las operaciones con números racionales.

Antes, recordemos las operaciones básicas:

Sumar

Suma racionales.png

Restar

Resta racionales.png

Multiplicar

Mult racionales.png

Dividir

Division racional.png

  • 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:

SumarNoEstatico.png

---

  • 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:

SumarEstatico.png

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)  

Herencia.png

  • 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.

Jerarquia clases 1.png



Icon key points.gif

Puntos clave

Todos los atributos y métodos de una clase superior (supertipo) que sean públicos o protegidos son heredados por todas las clases derivadas (subtipos).



  • Para establecer una jerarquía, usamos la palabra reservada extends en las clases que heredan.
  • Vamos a verlo con un ejemplo:

Jerarquia ambulatoria 1.png

  • 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:



Icon key points.gif

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 métodos del supertipo usando el operador parent junto con el operador de resolución de ámbito ::.



<?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.



Icon casestudy.gif
Herencia: gestión de personal en un ambulatorio
  • Se pide gestionar un ambulatorio.
  • Para ello haremos solo el diagrama de clases y su implementación a nivel básico (sin entrar en detalles).
  • Tras realizar el 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 representan en los siguientes diagramas:

  • Claramente vemos que todos ellos comparten varios elementos comunes.
  • Esto nos permite crear una clase genérica, por ejemplo personalAmbulatorio.
  • Posteriormente crearemos especializaciones de esta clase con los elementos particulares.

El diagrama podría quedar así:

JerarquiaPersonaAmbulatorio.png


  • Ver la aplicación ejecutándose:

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.



Icon casestudy.gif
Clase abstracta
  • Ejemplo ilustrativo:

Archivo:ClaseAbstracta.png



  • 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.



Icon define.gif
Clase abstracta
  • Es aquella clase que tiene uno o más métodos abstractos.




Icon define.gif
Método abstracto
  • Es un método que no tiene código asociado.
  • Su implementación se realizará en las clases derivadas.




Icon key points.gif

Puntos clave

Nunca podremos instanciar un objeto de una clase abstracta.
  • Esto es lógico, ya que ese objeto no tendría instrucciones para ejecutar sus métodos abstractos.



  • Vamos a plantear un ejemplo práctico:



Icon casestudy.gif
App de Geometría
{{{1}}}