JavaScript: Programación Orientado a Objetos — Fundamentos
Temario
- Fundamentos de Programación Orientada a Objetos (POO)
- Mi primera clase
- Alta cohesión y bajo acoplamiento
i. Fundamentos de Programación Orientada a Objetos (POO)
La Programación Orientada a Objetos (POO) es un paradigma de programación que utiliza la abstracción para crear modelos de objetos basados en el mundo real, utilizando paradigmas previamente establecidos, incluyendo modularidad, polimorfismo y encapsulamiento.
Los objetos pueden contener información y código relacionado, lo cual representa información acerca de lo que se está tratando de modelar, así como la funcionalidad o comportamiento que deseamos que tenga. Los datos de un Objeto (funciones) pueden almacenarse ordenadamente (encapsulación) dentro del paquete de un objeto (espacio de nombres), también se utilizan para almacenar datos que pueden ser enviados a otros objetos.
Las grandes ventajas que tiene POO:
- Mayor flexibilidad.
- Fácil mantenimiento.
- Desarrollo sencillo.
- Sencillo de entender al momento de programar.
Iniciemos con un ejemplo ambientado en la vida real (sin mencionar ningún tipo de lenguaje de programación).
Imaginemos que tenemos una persona, donde nos interesa :
- Nombre (nombre, apellido paterno, apellido materno)
- Edad
- Género
- Intereses.
Bien, veamos esto en forma de ficha:
Si hablamos técnicamente, podemos decir que tenemos una clase Persona, que tiene características (nombre, edad, género, intereses).
Teniendo nuestra clase, ya podemos generar objetos (personas) con características específicas, veamos un ejemplo:
Lo que estamos haciendo aquí es crear la instancia de objeto a partir de una clase, cuando se hace la instancia, se ejecuta una función constructora de la clase para crearla.
En el ejemplo de arriba podemos observar que de la clase Persona se hace una instancia de objeto a Persona1 y otra instancia de objeto a Persona2, podemos generar cuantas instancias de objeto necesitemos.
Ahora imaginemos un escenario, donde necesitemos roles imaginemos una escuela, donde se necesita Maestros y Alumnos (donde cada uno de ellos va a tener características únicas, pero queremos que ambos compartan las características de la clase Persona), entonces vamos por pasos:
Creamos la clase Maestro y la clase Alumno:
Ahora lo que vamos a hacer es heredar todas las características de la clase Persona a la clase Maestro y a la clase Alumno
La ventaja de usar la herencia entre clases es de que no necesitamos duplicar características que tenemos en común con otras clases, y podemos generar instancias de objeto a partir de esas clases; veamos un ejemplo:
ii. Mi primera clase
Hasta este punto ya sabemos y entendemos que es POO de manera básica, así como algunos conceptos como clase, instancia de objeto, herencia; ahora veamos todo esto en código.
Antes JavaScript era el único lenguaje convencional con herencia en prototipos (ver más), desde ES2015 ha decidido introducir clases, herencia, etc.
La estructura de una clase básica es la siguiente:
Es importante que el nombre de la clase inicie con mayúscula y tenga el estilo camel case.
Cuando creamos una clase debemos tener en cuenta lo siguiente:
- Propiedades
- Constructor
- Métodos
Para extender una clase, se debe usar la palabra reservada extends, la estructura es la siguiente:
Donde OtherName es el nombre de nuestra clase y AnyName es de quien vamos a extender la funcionalidad.
Ahora para hacer la instancia de objeto, la estructura básica es la siguiente:
var instantiatedObjectName = new AnyName();
Para instanciar la que tiene extend:
var instantiatedOtherObjectName = new OtherName();
Donde creamos una variable instantiatedObjectName y a ella le asignamos la instancia de la clase new AnyName().
Teniendo en cuenta la estructura básica de una clase, vamos a generar nuestra clase de los ejemplos que hicimos en fundamentos, para este caso crearemos la clase Person:
Ahora el ejemplo de la clase Teacher (recordemos que está extendida de la clase Person)
iii. Alta cohesión y bajo acoplamiento
Primero entendamos el significado de cohesión y acoplamiento.
Cohesión: Se refiere al grado en que los elementos de un módulo permanecen juntos. Por lo tanto, la cohesión mide la fuerza de la relación entre las piezas de funcionalidad dentro de un módulo dado. Por ejemplo, en sistemas altamente cohesivo de funcionalidad están muy relacionados. [ref]
Acoplamiento: Es la forma y nivel de interdependencia entre módulos de software; una medida de qué tan cercanamente conectados están dos rutinas o módulos de software; así como el grado de fuerza de la relación entre módulos. Por ejemplo acoplamiento es cuando un componente accede directamente a un dato que pertenece a otro componente. [ref]
Ya tenemos las definiciones de libro ahora vamos a explicarlas con ejemplos más claros, nos recomiendan tener en cuenta estos aspectos o principios de diseño “alta cohesión y bajo acoplamiento” para tener aplicaciones limpias y de fácil mantenimiento.
Cohesión: Entonces la baja cohesión (lo incorrecto) es cuando una clase realiza diversas acciones y no se centra exactamente en lo que debe hacer. Y alta cohesión (lo correcto) es cuando una clase realiza las acciones que debe hacer y no más.
Vamos a crear una clase llamada Users, la idea es que cumpla con la definición de baja cohesión, así que veamos como quedaría de manera incorrecta y correcta.
Podemos observar que nuestra clase hace otras tareas que quizás no sean correctas (addEmail, removeProduct), entonces la manera correcta sería algo así:
Acoplamiento: Alto acoplamiento (lo incorrecto) es cuando dos clases están relacionadas y donde querer hacer un cambio en alguna de ellas es muy complicado ya que le afecta demasiado a la otra. Y bajo acoplamiento (lo correcto) es cuando dos clases están relacionadas, pero al momento de hacer un cambio en una, no le afecta en nada a la otra.
Tenemos una clase Person:
Bien hasta aquí no tenemos ningún problema, entonces vamos a crear la clase Teacher donde va a extender de Person:
En vez de pasar inicializar las propiedades por el constructor del padre Person, o haciendo uso de los set que tiene, se decide modificar los valores de manera directa; hasta aquí parece que no tenemos ningún problema.
Supongamos que por una actualización se decide cambiar la propiedad de age por yearsOld:
Por acceder a las propiedades de manera directa, le va a afectar la clase Teacher, debido a que “alambró” o acopló los valores de manera incorrecta, entonces la instancia de objeto de Teacher nos arrojaría un undefined:
console.log(teacher.presented()); //output Hello my name is Mauricio García and I am undefined years old
Para cumplir de manera correcta con el principio de bajo acoplamiento, debemos de hacer uso del constructor que tiene la clase Person, o setear los valores por medio de sus métodos.
Haciendo este desacoplamiento si sufre algún cambio la clase Person es menos probable que afecten los cambios, ya que estamos haciendo uso correcto de su constructor y/o métodos.
Simplificando Cohesión y acoplamiento
Estos aspectos van mucho de la mano con el diseño de la aplicación, veamos el siguiente diagrama:
Al momento de trabajar en una aplicación, el escenario ideal es tener bajo acoplamiento y alta cohesión, pero muchas veces al intentarlo podemos terminar en el lado opuesto alto acoplamiento y baja cohesión cuando nos llega a pasar esto (es más común de lo que imaginas), entonces lo que debemos hacer es balancear ambos aspectos, pero ¿como?, ¿a quien o como damos la prioridad?, ¿hasta qué punto está bien o mal?
Inclinando la balanza hacia la alta cohesión
Cuando el código está bien cohesionado tiende a ser muy fácil de entender, es más limpio ya que tiene pocas o nulas dependencias con otras clases y también tiene propiedades bien definidas.
Pensemos la clase como una caja negra, donde tiene propiedades y métodos de entrada, y eventos de salida, esto es genial por que no nos va a interesar como funciona por dentro ya que el resultado de salida es el esperado y no importa si deciden hacer refactorización, tomando en cuenta que la salida de datos siga siendo la esperada (veremos más adelante como solucionar esta parte, spoiler: patrones de diseño).
Con las librerías/frameworks modernos (react, angular, vue, etc) se están generando super componentes donde se junta DOM, CSS y JS como uno solo, esto esta muy bien ya que hacer uso de el componente es mucho más fácil, pero… nos limita en cuanto a flexibilidad ya que estamos acoplando responsabilidades que quizás podríamos separar (CSS de un lado y JS de otro), y aquí… es donde empezamos a fallar en la alta cohesión ya que tendemos a hacer súper componentes que haga mil cosas que poco tienen que ver con el. Recordemos que entre mas acoplado este la funcionalidad, más complicado será actualizarlo.
Inclinando la balanza hacia el bajo acoplamiento
Cuando nos inclinamos por el bajo acoplamiento, lo que va a suceder es de que vamos a tener componentes más pequeños, más definidos y más fáciles de entender; suena genial ¿no?, ya que vamos a tener mucho mas componentes reutilizables.
El problema surge cuando esos componentes empiezan a ser reutilizados en muchos lados, cuando iniciamos con la ventaja de tener componentes desacoplados, estos van a terminar acoplándose a los diversos componentes que los están extendiendo y aquí es donde se empieza a complicar, y aún más cuando intentemos comprender cada uno de los componentes de manera individual y la relación que tienen con los demás.
Hasta aquí ya entiendes los conceptos básicos de POO, así como crear una clase sencilla en JavaScript y por si esto no fuera suficiente también entiendes los principios de alta cohesión y bajo acoplamiento.
En la siguiente entrega vamos a ver POO — Programación basada en prototipos.
Bibliografía y links que te puede interesar…