JS: Introducción a la POO
Teoría: Encapsulación
De todas las posibilidades de la POO, hay una básica que la mayoría de los programadores asocian con la POO. Se llama encapsulación. La encapsulación es la combinación de funciones y datos dentro de una estructura, cuyo estado interno (datos) está oculto al mundo exterior (este aspecto lo veremos más adelante). Estas funciones se llaman métodos. Ya nos hemos encontrado con ellos muchas veces y, como has notado, se utilizan ampliamente en JavaScript.
La parte difícil
Antes de que profundicemos en la encapsulación, debemos hacer una pequeña digresión sobre la terminología y la confusión en la comunidad de desarrolladores. Esto es especialmente importante, considerando que muchos estudiantes ya vienen con conocimientos de diferentes fuentes. Si te resulta difícil entender lo que se explica en el siguiente párrafo, simplemente ignóralo y vuelve a él al final del curso.
En muchos recursos, la encapsulación se entiende como ocultación de datos (data hiding) del acceso externo directo (generalmente mediante palabras clave como private, protected). Además, este es el significado que te pedirán en una entrevista, pero solo es correcto parcialmente. A pesar de que esta es una definición común, es importante distinguir entre la combinación de datos y métodos y la ocultación de esos datos. Hay lenguajes, como Python, en los que hay combinación de datos pero no ocultación de datos. Además, si se agrega ocultación de datos a estos lenguajes, la arquitectura del programa no cambiará, pero si se separan los datos y los métodos, se tendría que reescribir casi todo el código. La misma situación ocurre con los lenguajes que tienen ocultación de datos. Si se elimina, no cambiará mucho, excepto que los desarrolladores tendrán que tener más cuidado al trabajar con objetos.
En resumen, la encapsulación implica tanto la combinación como la ocultación, cuando está presente. Donde no está presente, es simplemente una combinación. En este curso, distinguiremos entre encapsulación (entendiendo solo la combinación de datos y funciones) y ocultación de datos, para poder discutir estas características de forma independiente. De lo contrario, habría confusión sobre lo que se quiere decir cuando se menciona el término encapsulación.
Se explica por qué se necesita la ocultación de datos en la lección Invariantes.
Fin de la parte difícil :)
Hablaremos sobre cómo funcionan los métodos internamente en la próxima lección. Ahora, veamos las características externas de los métodos.
Trabajar con métodos en lugar de funciones tiene un efecto inesperado: se puede implementar el autocompletado de métodos en los editores. Esto reduce la carga mental y es muy apreciado por los programadores. Existe la teoría de que esta característica de los métodos es la razón de su popularidad en la POO (aunque no está confirmada, es muy probable).
En lenguajes con un sistema de módulos desarrollado, el autocompletado también está disponible al trabajar con funciones regulares. Sin embargo, en esos casos, primero debes escribir el nombre correcto del módulo. Un ejemplo en Elixir: User.getName(user). Por otro lado, hay lenguajes con Unified Function Call (como Nim), donde se pueden llamar funciones regulares como métodos y obtener autocompletado.
Otra característica es bastante controvertida. Para muchos desarrolladores, el código con métodos parece "más natural". Desde su punto de vista, las abstracciones basadas en datos solo se pueden construir sobre la base de métodos. Si no se combinan datos y funciones en un solo lugar, la abstracción no es posible. Esta percepción surge debido a una experiencia limitada. Por lo general, estos desarrolladores nunca han trabajado fuera de los lenguajes de programación orientados a objetos populares y en su lenguaje, las abstracciones basadas en funciones son antinaturales e incluso imposibles.
Por supuesto, esto no es cierto. Basta con tomar el curso JS: Abstracción de datos para darse cuenta de ello. Las abstracciones y la modelización del mundo real existen no solo en la POO. Han existido antes y seguirán existiendo después.
Intenta imaginar agregar amigos al estilo de la POO. ¿Quién debe agregar a quién (el primer amigo al segundo o el segundo al primero) y cómo evitar la recursión al agregarse mutuamente?
La tercera característica de los métodos es más interesante. Realmente ayuda a hacer el trabajo con el código más fácil y el código en sí es más corto. Al trabajar con objetos, no es necesario importar nada adicional, como en el caso de las funciones. Cualquier función a la que se le pase un objeto puede llamar a sus métodos como quiera. Si estuviéramos trabajando con funciones, tendríamos que importar las funciones necesarias adicionalmente. Esta característica no se obtiene de forma gratuita, limita la extensibilidad de los objetos (sobre esto hablaremos en las próximas lecciones).
¿Y qué hacer en caso de que no haya un objeto, como en el ejemplo anterior? Los desarrolladores de lenguajes y bibliotecas abordan esto de diferentes maneras. En JavaScript, las funciones regulares y los métodos pueden coexistir sin problemas. Esto ocurre de manera similar en Python. En Ruby y PHP (en los frameworks modernos), las funciones regulares ya no se ven tan naturales, aunque todavía se pueden crear. En Java, directamente no hay forma de crear funciones regulares. Cualquier función será un método. Por lo tanto, en Java, se crean objetos prácticamente para cualquier cosa. Esto hace que el programa sea mucho más grande y dificulta la implementación de cosas simples. Pero hay otros lenguajes. En Elixir y Clojure, los métodos simplemente no existen en el sentido actual y, lo más importante, no son necesarios allí, y el código sigue siendo conciso, simple y extensible.
Para simular funciones regulares en Java, se utilizan métodos estáticos. Permiten trabajar sin crear objetos.
La cuarta característica son las cadenas encadenadas. Recuerda esta llamada:
Este método devuelve una nueva cadena que también tiene métodos, por lo que se pueden llamar. Por ejemplo:
Y ahora un poco de magia. ¿Qué pasa si no creamos variables intermedias y hacemos las llamadas directamente? Probémoslo:
El código se vuelve más compacto y en algunos casos será más comprensible. Siempre se puede dividir este código en varias líneas:
Estas cadenas encadenadas se pueden construir incluso si se devuelve un valor de otro tipo. En ese caso, se pueden utilizar métodos del tipo correspondiente:
Estas cadenas encadenadas tienen un nombre especial: Interfaz fluida.
Al igual que casi todo en la comprensión moderna de la POO, las cadenas encadenadas no son algo exclusivo. Además, repiten algo llamado tubería (pipeline). Si estás familiarizado con la línea de comandos, es probable que hayas visto este código varias veces:
Esta cadena de comandos pasa los datos de izquierda a derecha a través de diferentes procesadores. La propia idea proviene de las matemáticas y apareció mucho antes de la programación. En muchos lenguajes, el pipeline se implementa como una construcción del lenguaje (o un macro en Lisp). Es un concepto tan exitoso que ahora se está tratando de integrar en muchos lenguajes. Por ejemplo, está presente en F#, OCaml, Elixir, Elm, Julia, Hack. Actualmente, el pipeline está en etapa de consideración en JavaScript. Echa un vistazo al ejemplo: