miércoles, 30 de octubre de 2019

Paradigmas y tipos de lenguajes informáticos (3 de 3). Orientación a Objetos


En el paradigma orientado a objetos ya nos acercamos más a como organizamos el pensamiento las personas. Nosotros para comprender el mundo real no nos fijamos muchos en los detalles, si no que abstraemos y clasificamos lo que nos rodea para poder comprenderlo, a cada “ser” le atribuimos propiedades y en base a que características de dichas propiedades les conceden, sabemos cómo interactuar con ellos.

Un ejemplo de esto son, los coches, poca gente sabe (en profundidad) cómo funciona el motor de un coche, o su sistema eléctrico, pero mucha gente sabe como operar un coche, incluso no tendría problemas para cambiar de modelo de coche y poder conducirlo inmediatamente, a pesar de que existe centenares de modelos de coches; automáticos, estándares, y de características muy variadas (incluso sin consideramos detalles estéticos como el color). El hecho de que una vez que hemos aprendiendo a conducir un coche, podamos sin mucho problemas, conducir varios modelos de coche, no implica que sepamos cómo funciona un coche realmente, si no que sabemos que es lo que estos pueden hacer, y en base a esto, podemos utilizarlo.

La POO, consiste en clasificar nuestros problemas, en “entidades”, que tienen características (llamadas atributos), y comportamientos conocidos, con los que podemos interactuar, a la vez que tienen características y comportamientos ocultos que no son necesarios conocer para poder interactuar con los anteriores.

El cómo se relacionen a través de comportamientos y características todas estas entidades es lo que conocemos como programación orientada a objetos.

Las entidades con sus características definidas y establecidas se llaman objetos, y los objetos de un mismo tipo son clasificadas en clases, por ejemplo si mi coche es un Nissan Versa Blanco del 2017, ese sería considerado el objeto, el modelo Nissan Versa seria la clase, con las características de color y año.

Una clase posee



  • Atributos: Representa el estado de la clase (una vez convertida en objeto), es lo que le da “personalidad” a la clase (en el ejemplo del coche seria el año o el color). Para acceder a los atributos, según el lenguaje, pueden usarse métodos o propiedades.
  • Mensajes: Las objetos pueden definir mensajes, que son instrucciones para realizar acciones, esto está representado por métodos (funciones y procedimiento), que pueden recibir cero o más parámetros.
  • Relaciones: las clases tienen relaciones de diversos tipos con otras clases:
    • Asociación: Dos clases (o más) están asociadas de alguna forma, puede ser que una clase sea un conjunto de otra clase (como una lista), o que sus atributos estén compuestos de otras clases.
    • Dependencia: Una clase usa a la otra.
    • Generalización/Especialización: Aquí entran todos los mecanismos de herencia

Características de la programación orientada a objetos



  • Encapsulamiento/Ocultación: La programación modular, ya tenía lago de encapsulamiento y ocultación.

    El encapsulamiento consístete en juntar en un mismo lugar todo lo que define a un objeto, esto es los datos (atributos), y la funcionalidad (métodos), de forma indivisible (uno de los grandes errores de la programación es considerar que los datos, deben estar aislados del negocio, esto es incorrecto, en la POO, los datos y el negocio, son una entidad unificada, un objeto, no tiene sentido, si no tiene datos y si no tiene negocio), todo esto hace referencia a la cohesión de una clase.

    El ocultamiento a su vez, significa que la clase debe mostrar lo menos posible sobre sí misma. Su funcionamiento y estructuras internas, no le interesa al resto de las clases, cuando menos expongamos más mantenible va a ser la clase, esto refuerza el bajo acoplamiento.
  • Herencia: es una de las principales relaciones entre clases, permite extender una clase creando otra que “herede” todas sus características y comportamientos, pero permitiéndola agregar nueva funcionalidad. La clase de la cual hereda se llama “clase” madre (o base) y la otra clase “hija”. En el caso del coche, la clase “coche”, seria la clase madre,  una clase con ciertas características comunes a todos los coches, pero la marca y el modelo seria la clase hija, “Nissan Versa”. Es posible que luego se hagan más coches que hereden de la marca, como particularidades o agregados por ejemplo podemos tener un “Nissa Versa Sense” que herede del anterior. El coche como tal, el que conducimos, es el objeto de clase, la “instancia” en sí.
  • Polimorfismo: El polimorfismo en la programación siempre hace referencia a que “algo” pueda tener distintas “caras”, según el contexto y la necesidad. En el POO, significa que se puedan usar las clases madre, sin preocuparnos la instancia de la clase hija que realmente es, por ejemplo podemos tener una función que envía “coches” al extranjero, sin preocuparnos si es un Nissan, un volvo, o un Renault, porque es irrelevante, lo que importa es que son coches (Aquí lo relevante serían sus dimensiones)

Ejemplo en C# de programación orientada a objetos.


Vamos a hacer un ejemplo con una colección de clases basadas en polígonos (cuadrados, rectángulos, etc.).Veamos el diagrama:



Polígono es una clase abstracta que representa una figura geométrica, que tiene lados, y como también un área, el cómo se calcula esta área depende del tipo de figura en sí, vemos que esta es una clase “incompleta”, sabe que es lo que tiene que hacer (Calcular un área), pero no sabe cómo hacerlo, de allí lo abstracto de la clase.

Clase Abstracta Polígono
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/// <summary>
/// Representa un poligo, un poligono es una idea abstracta, hasta que no tiene lados y angulos, no es realmente un poligono.
/// </summary>
internal abstract class Poligono
{
    /// <summary>
    /// Numero de lados del poligono.
    /// </summary>
    public int Lados
    {
        get;
        private set;
    }
 
    /// <summary>
    /// Constructor del poligono.
    /// </summary>
    /// <param name="lados">Numero de lados</param>
    public Poligono(int lados)
    {
        this.Lados = lados;
    }
 
    /// <summary>
    /// Calcula la area, es una funcion abstracta, segun la figura la area se calcula de forma diferente.
    /// </summary>
    /// <returns>Area.</returns>
    public abstract double CalcularArea();
}
 
 
/// <summary>
/// Representa un poligo, un poligono es una idea abstracta, hasta que no tiene lados y angulos, no es realmente un poligono.
/// </summary>
internal abstract class Poligono
{
    /// <summary>
    /// Numero de lados del poligono.
    /// </summary>
    public int Lados
    {
        get;
        private set;
    }

    /// <summary>
    /// Constructor del poligono.
    /// </summary>
    /// <param name="lados">Numero de lados</param>
    public Poligono(int lados)
    {
        this.Lados = lados;
    }

    /// <summary>
    /// Calcula la area, es una funcion abstracta, segun la figura la area se calcula de forma diferente.
    /// </summary>
    /// <returns>Area.</returns>
    public abstract double CalcularArea();
}

Clase Rectángulo: Un rectángulo es propiamente un polígono, que ya tiene entidad, ya es algo concreto, la clase como tal tiene ancho y largo, y una forma específica de calcular su área.

Clase Rectángulo
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/// <summary>
/// Representa un rectangulo, es un poligono que tiene ancho y largo.
/// </summary>
internal class Rectangulo : Poligono
{
    /// <summary>
    /// Ancho.
    /// </summary>
    public int Ancho
    {
        get;
        private set;
    }
 
    /// <summary>
    /// Largo.
    /// </summary>
    public int Largo
    {
        get;
        private set;
    }
 
    /// <summary>
    /// Constructor del rectangulo.
    /// </summary>
    /// <param name="ancho">Ancho.</param>
    /// <param name="largo">Largo.</param>
    public Rectangulo(int ancho, int largo) : base(4)
    {
        this.Ancho = ancho;
        this.Largo = largo;
    }
 
    /// <summary>
    /// Calcula el area del rectangulo.
    /// </summary>
    /// <returns>Area</returns>
    public override double CalcularArea()
    {
        return Ancho * Largo;
    }
}
 
 
/// <summary>
/// Representa un rectangulo, es un poligono que tiene ancho y largo.
/// </summary>
internal class Rectangulo : Poligono
{
    /// <summary>
    /// Ancho.
    /// </summary>
    public int Ancho
    {
        get;
        private set;
    }

    /// <summary>
    /// Largo.
    /// </summary>
    public int Largo
    {
        get;
        private set;
    }

    /// <summary>
    /// Constructor del rectangulo.
    /// </summary>
    /// <param name="ancho">Ancho.</param>
    /// <param name="largo">Largo.</param>
    public Rectangulo(int ancho, int largo) : base(4)
    {
        this.Ancho = ancho;
        this.Largo = largo;
    }

    /// <summary>
    /// Calcula el area del rectangulo.
    /// </summary>
    /// <returns>Area</returns>
    public override double CalcularArea()
    {
        return Ancho * Largo;
    }
}

Clase cuadrado: El cuadrado es a su vez un tipo de rectángulo en el que los lados miden lo mismo, aquí vemos una relación de herencia en la que un cuadrado es una particularización de un rectángulo y a su vez un rectángulo es un tipo de polígono.

Clase Cuadrado
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/// <summary>
/// Representa un cuadrado, un cuadrado es un rectangulo que tiene sus cuadro lados iguales.
/// </summary>
internal class Cuadrado : Rectangulo
{
    /// <summary>
    /// Tamaño del lado (todos los lados son iguales).
    /// </summary>
    public int TamanoLado
    {
        get
        {
            return Largo;
        }
    }
 
    /// <summary>
    /// Constructor de cuadrado.
    /// </summary>
    /// <param name="tamanoLado">tamaño de los lados.</param>
    public Cuadrado(int tamanoLado) : base(tamanoLado, tamanoLado)
    {
    }
}
 
 
/// <summary>
/// Representa un cuadrado, un cuadrado es un rectangulo que tiene sus cuadro lados iguales.
/// </summary>
internal class Cuadrado : Rectangulo
{
    /// <summary>
    /// Tamaño del lado (todos los lados son iguales).
    /// </summary>
    public int TamanoLado
    {
        get
        {
            return Largo;
        }
    }

    /// <summary>
    /// Constructor de cuadrado.
    /// </summary>
    /// <param name="tamanoLado">tamaño de los lados.</param>
    public Cuadrado(int tamanoLado) : base(tamanoLado, tamanoLado)
    {
    }
}

Veamos el siguiente código donde usamos todos los elementos en conjunto

En el siguiente método se suman el área de varios polígonos y se devuelve como resultado, notase que puede ser un polígono de cualquier forma, a la función eso le da igual, solo llama a la función de CalcularArea que es diferente en cada tipo de polígono (eso se llama polimorfismo, a pesar de que nuestra función trabaja con la clase base, la abstracta, se ejecuta cada uno de los métodos de CalcularArea de cada polígono).

 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
/// <summary>
/// El metodo suma las areas de un conjunto de poligonos, es de notar que no se especifica el tipo de poligono.
/// </summary>
/// <param name="poligonos"></param>
/// <returns></returns>
private static double SumarAreas(params Poligono[] poligonos)
{
    double result = 0;
    foreach (Poligono poligono in poligonos)
    {
        //Ejecuto por cada poligo su función calcular area, que es diferente segun el caso.
        result += poligono.CalcularArea();
    }
    return result;
}
 
 
/// <summary>
/// El metodo suma las areas de un conjunto de poligonos, es de notar que no se especifica el tipo de poligono.
/// </summary>
/// <param name="poligonos"></param>
/// <returns></returns>
private static double SumarAreas(params Poligono[] poligonos)
{
    double result = 0;
    foreach (Poligono poligono in poligonos)
    {
        //Ejecuto por cada poligo su función calcular area, que es diferente segun el caso.
        result += poligono.CalcularArea();
    }
    return result;
}

Veamos cómo se consume toda esta funcionalidad

 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
//Creo los poligonos, es de notar que cada poligono es de un tipo diferente.
 
Poligono rectangulo = new Rectangulo(2, 5);
Poligono cuadrado = new Cuadrado(8);
Poligono triangulo = new Triangulo(10, 12);
Poligono pentagono = new PentagonoRegular(2);
 
// Muestro el area de cada poligono
 
Console.WriteLine("El area de rectangulo es: " + rectangulo.CalcularArea().ToString("0.##"));
Console.WriteLine("El area del cuadrado es: " + cuadrado.CalcularArea().ToString("0.##"));
Console.WriteLine("El area del triangulo es: " + triangulo.CalcularArea().ToString("0.##"));
Console.WriteLine("El area del pentagono es: " + pentagono.CalcularArea().ToString("0.##"));
 
// Muestro el area tota,
Console.WriteLine("El area total es: " + SumarAreas(rectangulo, cuadrado, triangulo, pentagono).ToString("0.##"));
Console.ReadLine();
 
 
//Creo los poligonos, es de notar que cada poligono es de un tipo diferente.

Poligono rectangulo = new Rectangulo(2, 5);
Poligono cuadrado = new Cuadrado(8);
Poligono triangulo = new Triangulo(10, 12);
Poligono pentagono = new PentagonoRegular(2);

// Muestro el area de cada poligono

Console.WriteLine("El area de rectangulo es: " + rectangulo.CalcularArea().ToString("0.##"));
Console.WriteLine("El area del cuadrado es: " + cuadrado.CalcularArea().ToString("0.##"));
Console.WriteLine("El area del triangulo es: " + triangulo.CalcularArea().ToString("0.##"));
Console.WriteLine("El area del pentagono es: " + pentagono.CalcularArea().ToString("0.##"));

// Muestro el area tota,
Console.WriteLine("El area total es: " + SumarAreas(rectangulo, cuadrado, triangulo, pentagono).ToString("0.##"));
Console.ReadLine();

Como vemos se crean los polígonos, indicando el tipo, pero asociándolo a una variable de tipo “polígono”, usando características polimórficas, vamos ejecutando cada uno de los métodos CalularArea, hasta que llamamos a SumarAreas, donde le pasamos el listado de figuras geométricas sin ningún orden en particular, puesto que esta función solo recibe polígonos.

¿Qué fue antes la clase o el objeto?


Una broma que encontré en internet, emulando a la famosa frase “que fue antes el huevo o la a pesar gallina”, es “¿Qué fue antes la clase o el objeto?”. A pesar que es un meme, se me hizo practico contestar la pregunta

Lo primero fue el objeto, esto es porque había datos y comportamiento antes de los que los clasificáramos, esto es además, el proceso de análisis normal, primero identificamos los elementos que queremos programar en un ejemplo práctico y después lo abstraemos para generalizarlo. Entonces podemos decir que la clase es realmente, una abstracción del objeto.




Si te ha gustado la entrada, ¡Compártela! ;-)

Nota: Puedes encontrar todo el código fuente de este artículo en https://github.com/jbautistamartin/ParadigmasTiposLenguajes

No hay comentarios:

Publicar un comentario