---
layout: post
title: "POO Lección 3: Conceptos principales"
tags: [POO, OOP, Programación Orientada a Objetos, Object Oriented Programming]
---
La `programación orientada a objetos` esta basada en ciertos pilares,
conceptos que lo diferencian de otros paradigmas de programación.
- Abstraction
- Encapsulation
- Polymorphism
- Inheritance
### Abstracción
La mayoría del tiempo cuando creamos programas con **POO**, creamos objetos basados en objetos de la vida real.
Sin embargo, los objetos del programa no representan los reales con el 100% de precision (y es raro que esto pueda suceder)
En cambio, tus objetos solo modelan atributos y comportamientos del objeto real en un contexto especifico que necesitamos, ignorando el resto.
Por ejemplo, una clase `Airplane` podríamos considerar su existencia tanto un simulador de vuelos como en una aplicación de reservas de avión.
Pero en el contexto de la aplicación de reservas, deberias preocuparte solo sobre mostrar el mapa de asientos y si estan disponibles o no.
<p align="center"><img src="https://user-images.githubusercontent.com/22304957/71416078-b7fea180-263d-11ea-8940-51e7b67deade.png"/></p>
La `Abstracción` es un modelo de un fenomeno u objeto del mundo real, limitado a un contexto especifico, que representa todos los detalles relacionados a ese contexto con una alta precisión y omitiendo todo el resto.
### Encapsulación
Para arrancar un motor del auto, solamente necesitamos presionar un botón o girar una llave dependiendo el tipo de auto.
No necesitamos conectar cables debajo del capó, girar el cigüeñal, los cilindros, e iniciar el ciclo de potencia del motor. Todos estos detalles estan ocultos debajo del capó del auto. Solamente tenemos una simple interfaz: un boton de encendido, la dirección del auto y algunos pedales.
Esto basicamente nos ilustra como un objeto tiene una `interfaz`, es decir, una parte publica a la que pueden acceder quienes la utilizan, para lograr interaccionar con el objeto en cuestión.
`Encapsulación` es la habilidad de los objetos para esconder partes de sus estados y comportamientos, de otros objetos, exponiendo solamente una `interfaz` limitada al resto del programa, con los funcionamientos que deseamos exponer al público o al cliente de nuestra clase.
Encapsular algo significa hacerlo `private`, y que esto sea accesible solamente desde dentro de la clase mediante métodos.
Existe un tipo algo menos restrictivo llamado `protected`, el cual permite además que los hijos de esa clase, si existen, que puedan acceder a los estados y/o métodos de esa clase padre.
Interfaces y clases/métodos abstractos en la mayoría de los lenguajes de programación estan basados en los conceptos de abstracción y encapsulación. En los lenguajes de POO modernos, los mecanismos de interfaces (usualmente declarados con la palabra reservada `interface`) dejan que definamos contratos de interacción entre objetos.
Esta es una de las razones por las que las interfaces solamente se preocupan sobre el comportamiento de los objetos y el porque no podes declarar un campo en la interfaz.
Imaginemos que tenemos una interfaz `FlyingTransport` con un metodo `fly(origin, destination, passengers)`.
Cuando diseñamos un simulador de transporte, podemos restringir que la clase `Airport` trabaje solo con objetos que implementan la interfaz `FlyingTransport`. Esto te asegura que cada objeto pasado al objeto `Airport`, sin importar si es un `Airplane`, un `Helicopter` o un maldito `DomesticatedGryphon`
estara habilitado para aterrizar o despegar de este `Aeropuerto`.
<p align="center"><img src="https://user-images.githubusercontent.com/22304957/71416687-4d9b3080-2640-11ea-9780-875126c0e585.png"/></p>
Podemos cambiar la implementación del metodo `fly` de la forma que querramos. Siempre y cuando la firma del método (origin, destination, passengers) se mantenga de la misma manera en que fue declarada en la interfaz.
Todas las instancias (es decir, un objeto particular de la clase) de la clase `Airport` pueden funcionar correctamente siempre que los objetos que reciban respeten la interfaz `FlyingTransport`
correctamente.
### Herencia
La herencia es la habilidad de crear clases por arriba de las existentes. El principal beneficio de la herencia, es la reutilización de codigo. 
Si deseas crear una clase que sea ligeramente diferente de una existente, no hay necesidad de duplicar el código. En cambio, podemos `extender` la
clase existente y poner la funcionalidad adicional en la subclase, que hereda los campos y métodos de la superclase.
La consecuencia de utilizar herencia es que las subclases tienen la misma interfaz que sus clases padres. Además no podemos esconder un metodo en la subclase si este fue declarado en la superclase.
También estamos obligados a implementar todos los métodos abstractos que esten declarados en el padre, así estos no tengan sentido en nuestra subclase (quedara vacio en tal caso, aunque no es una buena práctica).
<p align="center"><img src="https://user-images.githubusercontent.com/22304957/71417096-5260e400-2642-11ea-86af-c88854c5ff45.png"/></p>
En la mayora de los lenguajes de programación una subclase puede extender solamente de una clase.
Por otro lado, cualquier clase puede `implementar` muchas interfaces al mismo tiempo.
Pero, como mencionamos antes, si la superclase implementa una interfaz, todos los hijos o subclases deben también implementarlos.
### Polimorfismo
Veremos algunos ejemplos sobre animales. La mayoría de los `Animals` pueden hacer sonidos.
Podemos anticipar que todas las subclases van a necesitar sobreescribir el metodo base `makeSound` para tener su propio comportamiento, entonces cada subclase puede emitir el sonido correcto según el animal que sea; por lo que podemos declarar este método de manera abstracta para obligar su propia implementación en cada subclase.
Esto permite que omitamos cualquier implementación por defecto en la superclase lo cual no tendría sentido alguno (ya que no hay un sonido generico para "Animales"), y además fuerza a las subclases a tener su propio comportamiento, de lo contrario no podrán ser del tipo `Animals`.
<p align="center"><img src="https://user-images.githubusercontent.com/22304957/71417369-c0f27180-2643-11ea-84e8-671ad0feebcb.png"/></p>
Imaginemos que ponemos muchos gatos y perros dentro de una gran bolsa. Luego, con los ojos cerrados, tomamos los animales uno a uno fuera de la bolsa.
Luego de tomar un animal de la bolsa, nosotros no estamos seguros de que tipo es (sabemos solamente que es del tipo Animal). Sin embargo, si lo acariciamos lo suficiente, el animal emitirá un sonido específico, de alegría seguramente, pero dependiendo de su clase concreta tendrá la capacidad de emitir un sonido diferente según sea un gato o un perro en este caso.
```
Posible código de implementación:
1 bag = [new Cat(), new Dog(), new Cat()];
2
3 foreach (Animal particularAnimal : bag)
4 particularAnimal.makeSound()
5
6 // Meow!
7 // Woof!
8 // Meow!
```
Este programa no sabe el tipo concreto que reside dentro de la variable `particularAnimal`; pero, gracias a el mecanismo llamado `polimorfismo`,
el programa puede seguir hasta la subclase del objeto actual al que le queremos ejecutar una acción determinada, donde el método correspondiente es ejecutado con su apropiada implementación, es decir, por más que sea del tipo `Animals` el programa recorre hasta la implementación concreta de esa interfaz, en el primer caso iría hasta la clase `Cat` buscaría su método `makeSound` correspondiente y ejecutaría ese comportamiento, luego en la segunda haría lo mismo pero en este caso sabría que es un `Dog` por lo tanto tendrá otro comportamiento, pero el mecanismo es el mismo.
`Polimorfismo` es la habilidad del programa para detectar la clase real del objeto y llamar a su implementación aún cuando el tipo real es "desconocido" (Ya que pareciera ser del tipo `Animals`) en el contexto actual.
También puedes pensar sobre el polimorfismo como la habilidad de un objeto de "pretender" ser algo más, usualmente una clase que `extiende` o `implementa` una interface.
En nuestro ejemplo, los perros y gatos en la bolsa pretenden ser animales genericos.
Basicamente estos son los principios en los que se sustenta la programación orientada a objetos,
después existen más caracteristicas adicionales según el lenguaje de programación que lo implementa que ayudan mucho a implementar más facilmente estas bases, pero entendiendo esto es la gran base para poder continuar con lo demás.