Action à distance.
L’action à distance est un antipatron où du code situé dans une partie du programme modifie en silence le comportement d’une partie éloignée et sans lien.
L’action à distance est un anti-patron en informatique dans lequel le comportement d’une partie d’un programme varie énormément en fonction d’opérations difficiles, voire impossibles, à identifier dans une autre partie du programme.
La façon d’éviter les problèmes associés à l’action à distance consiste à adopter une conception appropriée, qui évite les variables globales et ne modifie les données que de manière contrôlée et locale, ou à recourir à un style de programmation purement fonctionnelle avec transparence référentielle.
Le terme est emprunté à la physique, où l’action à distance désigne l’influence mutuelle d’objets sans intermédiaire visible.
Les bogues logiciels dus à l’action à distance peuvent survenir parce qu’un composant du programme fait quelque chose au mauvais moment, ou affecte quelque chose qu’il ne devrait pas. Il est toutefois très difficile de déterminer quel composant est responsable. Les effets de bord d’actions anodines peuvent placer le programme dans un état inconnu, de sorte que les données locales ne sont pas nécessairement locales. La solution dans ce scénario particulier consiste à définir quels composants doivent interagir avec quels autres. Une conception appropriée, qui définit précisément l’interface entre les parties d’un programme et qui évite les états partagés, peut largement éliminer les problèmes causés par l’action à distance.
Exemple historique : $[ en Perl
Cet exemple, tiré du langage de programmation Perl, illustre un cas particulièrement grave d’action à distance (notez que la variable $[ a été dépréciée dans les versions ultérieures de Perl) :
Les indices d’un tableau commencent normalement à 0 parce que la valeur de
$[est normalement 0 ; si vous fixez$[à 1, alors les tableaux commencent à 1, ce qui réjouit les programmeurs Fortran, et l’on voit donc des exemples comme celui-ci dans la page de manuelperl(3):foreach $num ($[ .. $#entry) { print " $num\t'",$entry[$num],"'\n"; }Et bien sûr, vous pourriez fixer
$[à 17 pour que les tableaux commencent à un nombre arbitraire comme 17 ou 4 au lieu de 0 ou 1. C’était un excellent moyen de saboter les auteurs de modules.Heureusement, la raison l’a emporté. Ces fonctionnalités sont aujourd’hui reconnues comme ayant été des erreurs. La liste de diffusion perl5-porters a désormais une expression toute faite pour de telles fonctionnalités : on les appelle « action à distance ». Le principe est qu’une déclaration dans une partie du programme ne devrait pas modifier radicalement et invisiblement le comportement d’une autre partie du programme.
— Mark Jason Dominus, Sins of Perl Revisited
Action à distance entre objets
Une programmation orientée objet appropriée repose sur des principes de conception qui évitent l’action à distance.
La loi de Déméter stipule qu’un objet ne devrait interagir qu’avec d’autres objets proches de lui. Si une action dans une partie distante du système est nécessaire, elle devrait être mise en œuvre en propageant un message. Une conception appropriée limite fortement les occurrences d’action à distance, ce qui contribue à des programmes maintenables. La tendance à créer une orgie d’objets résulte d’une mauvaise conception des interfaces, prenant peut-être la forme d’un objet dieu, de l’absence de véritables objets, ou du non-respect de la loi de Déméter.
L’un des avantages de la programmation fonctionnelle est que l’action à distance y est minimisée, parfois au point d’être tout bonnement impossible à exprimer dans le langage source.
Être conscient du danger de laisser l’action à distance s’introduire dans une conception, et savoir reconnaître sa présence, est utile pour développer des programmes corrects, fiables et maintenables. Étant donné que l’essentiel du coût d’un programme peut se situer dans la phase de maintenance, et que l’action à distance rend la maintenance difficile, coûteuse et sujette aux erreurs, il vaut la peine de faire l’effort de l’éviter lors de la conception.
Exemple
Ici, le module de rapports modifie un dictionnaire de configuration partagé et mutable. Rien ne change dans invoices.py, et pourtant, après le premier export CSV, toutes les factures s’impriment soudain avec quatre décimales.
# 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 # toutes les factures imprimées ensuite changent aussi
return [format_amount(r) for r in rows]
Le format est désormais une valeur immuable transmise explicitement :
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) # ne concerne que cet export
return [format_amount(r, fmt) for r in rows]
Une modification à un endroit ne peut plus se propager ailleurs ; des globales mutables partagées comme DEFAULTS correspondent à ce que la théorie du couplage appelle le couplage commun (voir Couplage fort).
Adapté de l’article Wikipédia “Action at a distance (computer programming)” (révision 1329485461), disponible sous licence CC BY-SA 4.0 ; le texte a été traduit et modifié, et des exemples ont été ajoutés.