JS: Abstracción de datos

Teoría: Creación de abstracciones

El sistema de coordenadas cartesianas no es la única forma de representación gráfica. Otra forma es el sistema de coordenadas polares.

El sistema de coordenadas polares es un sistema de coordenadas bidimensional en el que cada punto en el plano se define de manera única por dos números: el ángulo polar y el radio polar. El sistema de coordenadas polares es especialmente útil cuando las relaciones entre puntos son más fáciles de representar en términos de radios y ángulos; en el sistema de coordenadas cartesianas, más comúnmente conocido como sistema rectangular, estas relaciones solo se pueden establecer mediante ecuaciones trigonométricas. (c) Wikipedia

Sistema de Coordenadas Polares

Imagínate la siguiente situación. Estás desarrollando un editor gráfico (¡Photoshop!) y tu biblioteca para trabajar con primitivas gráficas se basa en el sistema de coordenadas cartesianas. En algún momento te das cuenta de que cambiar al sistema de coordenadas polares ayudará a hacer el sistema más simple y rápido. ¿Cuál será el costo de este cambio? Tendrás que reescribir casi todo el código.

const point = { x: 2, y: 3 };
const symmetricalPoint = { x: -point.x, y: point.y };

Esto se debe a que tu biblioteca no oculta la estructura interna. Cualquier código que use puntos o segmentos sabe cómo están estructurados internamente. Esto se aplica tanto al código que crea nuevas primitivas como al código que extrae partes de ellas. Cambiar la situación y ocultar la implementación es bastante simple utilizando funciones:

const point = makePoint(3, 4);
const symmetricalPoint = makePoint(-getX(point), getY(point));

En el ejemplo, vemos tres funciones makePoint(), getX() y getY(). La función makePoint() se llama constructor porque crea una nueva primitiva, y las funciones getX() y getY() se llaman selectores (selector), derivado de la palabra "select", que significa "extraer" o "seleccionar". Este pequeño cambio tiene consecuencias significativas. Lo más importante es que el código de aplicación (el que utiliza la biblioteca) no trabaja directamente con la estructura.

// No trabajamos así
const point = [1, 4]; // sabemos que es un array
console.log(point[1]); // acceso directo al array

// Sino así
const point = makePoint(3, 4); // no sabemos cómo está estructurado el punto
console.log(getY(point)); // accedemos a las partes solo a través de los selectores

Mirando el código, ni siquiera se puede decir qué representa internamente el punto, qué construcciones del lenguaje se utilizan (para esto se puede usar la impresión de depuración). Así es como construimos una abstracción de datos. La esencia de esta abstracción es que ocultamos la implementación interna. Se puede decir que crear una abstracción utilizando datos conduce a ocultar esos datos del código externo.

Aquí hay una forma de implementar una abstracción para trabajar con puntos:

const makePoint = (x, y) => ({ x, y });

const getX = (point) => point.x;
const getY = (point) => point.y;

Ahora podemos cambiar la implementación sin tener que reescribir todo el código (aunque aún puede ser necesario reescribir partes individuales). A pesar de que usamos la función makePoint(), que crea un punto basado en el sistema de coordenadas cartesianas (recibe las coordenadas x e y), internamente puede representarse en el sistema de coordenadas polares. En otras palabras, durante la construcción, se realiza una traducción de un formato a otro:

const makePoint = (x, y) => {
  // conversión
    return {
    angle: Math.atan2(y, x),
    radius: Math.sqrt(x ** 2 + y ** 2)
  };
};

Una vez que comienzas a trabajar a través de una abstracción de datos, no hay vuelta atrás. Siempre adhiérete a las funciones que has creado tú mismo. O a las que te proporciona la biblioteca que estás utilizando.