Logotipo de ConstructiCat
CodeBust.
Explorar la sección ▾

Acción a distancia.

La acción a distancia es un antipatrón en el que el código de una parte del programa cambia en silencio el comportamiento de otra parte lejana y sin relación.

La acción a distancia es un antipatrón de la informática en el que el comportamiento de una parte de un programa varía drásticamente en función de operaciones difíciles o imposibles de identificar en otra parte del programa.

La manera de evitar los problemas asociados a la acción a distancia es un buen diseño, que evite las variables globales y altere los datos solo de forma controlada y local, o el uso de un estilo de programación funcional pura con transparencia referencial.

El término procede de la física, donde la acción a distancia designa la influencia mutua entre objetos sin ningún intermediario visible.

Los errores de software debidos a la acción a distancia pueden surgir porque un componente del programa hace algo en el momento equivocado o afecta a algo que no debería. Sin embargo, es muy difícil rastrear qué componente es el responsable. Los efectos secundarios de acciones aparentemente inocentes pueden dejar el programa en un estado desconocido, de modo que los datos locales no son necesariamente locales. La solución en este escenario concreto es definir qué componentes deben interactuar con cuáles otros. Un buen diseño que defina con precisión la interfaz entre las partes de un programa, y que evite los estados compartidos, puede eliminar en gran medida los problemas causados por la acción a distancia.

Ejemplo histórico: $[ en Perl

Este ejemplo, del lenguaje de programación Perl, demuestra un caso especialmente grave de acción a distancia (obsérvese que la variable $[ quedó obsoleta en versiones posteriores de Perl):

Los índices de los arrays normalmente empiezan en 0 porque el valor de $[ normalmente es 0; si pones $[ a 1, entonces los arrays empiezan en 1, lo que hace felices a los programadores de Fortran, y por eso vemos ejemplos como este en la página de manual de perl(3):

foreach $num ($[ .. $#entry) {
    print "  $num\t'",$entry[$num],"'\n";
}

Y, por supuesto, podrías poner $[ a 17 para hacer que los arrays empiecen en un número arbitrario como 17 o 4 en lugar de en 0 o 1. Esta era una magnífica manera de sabotear a los autores de módulos.

Por fortuna, prevaleció la cordura. Ahora se reconoce que estas características fueron errores. La lista de correo perl5-porters tiene ahora una expresión para esas características: las llaman «acción a distancia». El principio es que una declaración en una parte del programa no debería alterar drástica e invisiblemente el comportamiento de otra parte del programa.

— Mark Jason Dominus, Sins of Perl Revisited

Acción a distancia entre objetos

Una programación orientada a objetos adecuada implica principios de diseño que evitan la acción a distancia.

La ley de Deméter establece que un objeto solo debería interactuar con otros objetos cercanos a él. Si se requiere una acción en una parte distante del sistema, esta debería implementarse propagando un mensaje. Un buen diseño limita drásticamente las apariciones de la acción a distancia, lo que contribuye a programas mantenibles. La presión por crear una orgía de objetos surge de un diseño deficiente de la interfaz, tal vez en forma de un objeto todopoderoso, de no implementar verdaderos objetos o de no respetar la ley de Deméter.

Una de las ventajas de la programación funcional es que la acción a distancia se minimiza, a veces hasta el punto de ser imposible de expresar en absoluto en el lenguaje de origen.

Ser consciente del peligro de permitir la acción a distancia en un diseño, y ser capaz de reconocer la presencia de la acción a distancia, es útil para desarrollar programas correctos, fiables y mantenibles. Dado que la mayor parte del coste de un programa puede recaer en la fase de mantenimiento, y que la acción a distancia hace que el mantenimiento sea difícil, costoso y propenso a errores, merece la pena dedicar esfuerzo durante el diseño a evitarla.

Ejemplo

Aquí el módulo de informes modifica un diccionario de configuración compartido y mutable. En invoices.py no cambia nada y, sin embargo, tras la primera exportación a CSV todas las facturas se imprimen de repente con cuatro decimales.

# settings.py
DEFAULTS = {"currency": "USD", "precision": 2}

# invoices.py
from settings import DEFAULTS

def format_amount(amount):
    return f"{amount:.{DEFAULTS['precision']}f} {DEFAULTS['currency']}"

# reports.py
from invoices import format_amount
from settings import DEFAULTS

def export_csv(rows):
    DEFAULTS["precision"] = 4  # también cambia cada factura que se imprima después
    return [format_amount(r) for r in rows]

Ahora el formato es un valor inmutable que se pasa de forma explícita:

from dataclasses import dataclass


@dataclass(frozen=True)
class Format:
    currency: str = "USD"
    precision: int = 2


def format_amount(amount, fmt=Format()):
    return f"{amount:.{fmt.precision}f} {fmt.currency}"


def export_csv(rows):
    fmt = Format(precision=4)  # solo afecta a esta exportación
    return [format_amount(r, fmt) for r in rows]

Un cambio en un sitio ya no puede filtrarse a otro; los datos globales mutables y compartidos como DEFAULTS son lo que la teoría del acoplamiento llama acoplamiento común (véase Acoplamiento fuerte).

Adaptado del artículo de Wikipedia “Action at a distance (computer programming)” (revisión 1329485461), disponible bajo la licencia CC BY-SA 4.0; el texto ha sido traducido y editado, y se han añadido ejemplos.