Remplacer les dépendances directes par la messagerie

Parfois, un module a juste besoin d'informer les autres que certains événements/changements s'y sont produits, et peu importe ce qu'il adviendra de ces informations plus tard.

Dans ce cas, les modules n'ont pas du tout besoin de "se connaître", c'est-à-dire de contenir des liens directs et d'interagir directement, mais il suffit juste d'échanger des messages (messages) ou des événements (événements).

Parfois, il semble que la communication du module via la messagerie soit beaucoup plus faible que la dépendance directe. En effet, comme les méthodes ne sont pas appelées, il n'y a pas d'informations sur les classes. Mais ce n'est rien de plus qu'une illusion.

Au lieu des noms de méthode, la logique commence à être liée aux types de message, à leurs paramètres et aux données transmises. La connectivité de ces modules est brouillée.

Auparavant, c'était comme : nous appelons des méthodes - il y a de la connectivité, nous n'appelons pas de méthodes - il n'y a pas de connectivité. Imaginez maintenant que le module A commence à envoyer des données légèrement différentes dans ses messages. Et en même temps, tous les modules dépendants de ces messages ne fonctionneront pas correctement.

Supposons, plus tôt, lors de l'ajout d'un nouvel utilisateur, le module d'autorisation a envoyé le message USER_ADDED, et après la mise à jour, il a commencé à envoyer ce message lors de la tentative d'enregistrement et indique en outre l'enregistrement réussi ou non dans les paramètres.

Par conséquent, il est très important de mettre en œuvre le mécanisme de message de manière très compétente. Il existe différents modèles pour cela.

Observateur. Il est utilisé dans le cas d'une dépendance un-à-plusieurs, lorsque de nombreux modules dépendent de l'état d'un - le principal. Il utilise le mécanisme de mailing, ce qui signifie que le module principal envoie simplement les mêmes messages à tous ses abonnés, et les modules intéressés par ces informations implémentent l'interface « abonné » et s'abonnent à la liste de diffusion.

Cette approche est largement utilisée dans les systèmes avec une interface utilisateur, permettant au cœur de l'application (modèle) de rester indépendant tout en informant ses interfaces associées que quelque chose a changé et doit être mis à jour.

Ici, le format des messages est standardisé au niveau du système d'exploitation, dont les développeurs doivent veiller à la rétrocompatibilité et à une bonne documentation.

L'organisation de l'interaction par la distribution de messages a un «bonus» supplémentaire - l'existence facultative d '«abonnés» aux messages «publiés» (c'est-à-dire envoyés). Un système bien conçu comme celui-ci permet d'ajouter/supprimer des modules à tout moment.

Bus de messagerie

Vous pouvez organiser l'échange de messages et utiliser le modèle Mediator pour cela d'une manière différente .

Il est utilisé lorsqu'il existe une dépendance plusieurs à plusieurs entre les modules. Le médiateur agit comme un intermédiaire dans la communication entre les modules, agissant comme un centre de communication et éliminant le besoin pour les modules de se référer explicitement les uns aux autres.

En conséquence, l'interaction des modules entre eux (« tous avec tous ») est remplacée par l'interaction des modules uniquement avec un intermédiaire (« un avec tous »). On dit que le médiateur encapsule l'interaction entre plusieurs modules.

Bus de messagerie

C'est ce qu'on appelle l' intermédiaire intelligent . C'est là que les développeurs commencent le plus souvent à ajouter leurs béquilles, qui influencent le comportement des modules individuels en activant/désactivant la réception de certains messages.

Un exemple typique de la vie réelle est le contrôle du trafic aéroportuaire. Tous les messages des avions vont à la tour de contrôle du contrôleur au lieu d'être envoyés directement entre les avions. Et le contrôleur prend déjà des décisions sur les avions qui peuvent décoller ou atterrir et, à son tour, envoie des messages aux avions.

Important! Les modules peuvent s'envoyer non seulement des messages simples, mais aussi des objets de commande. Une telle interaction est décrite par le modèle Command . L'essentiel est d'encapsuler une demande d'exécution d'une action spécifique en tant qu'objet distinct.

En fait, cet objet contient une seule méthode execute() , qui vous permet ensuite de passer cette action à d'autres modules pour exécution en tant que paramètre et généralement d'effectuer toutes les opérations avec l'objet command qui peuvent être effectuées sur des objets ordinaires.

Loi de Déméter

La loi de Déméter interdit l'utilisation de dépendances implicites : "L'objet A ne doit pas pouvoir accéder directement à l'objet C si l'objet A a accès à l'objet B et l'objet B a accès à l'objet C."

Cela signifie que toutes les dépendances dans le code doivent être "explicites" - les classes/modules ne peuvent utiliser que "leurs dépendances" dans leur travail et ne doivent pas les traverser vers d'autres. Un bon exemple est une architecture à trois niveaux. La couche d'interface doit fonctionner avec la couche logique, mais ne doit pas interagir directement avec la couche de base de données.

Brièvement, ce principe est également formulé de cette manière : "Interagissez uniquement avec des amis immédiats, et non avec des amis d'amis". Cela permet d'obtenir moins de cohérence du code, ainsi qu'une plus grande visibilité et transparence de sa conception.

La loi de Déméter met en œuvre le « principe de connaissance minimale » déjà mentionné, qui est à la base du couplage lâche et consiste dans le fait qu'un objet/module doit connaître le moins de détails possible sur la structure et les propriétés des autres objets/modules et quoi que ce soit en général, y compris ses propres composants .

Une analogie de la vie: si vous voulez que le chien coure, il est stupide de commander ses pattes, il vaut mieux donner la commande au chien, et elle s'occupera elle-même de ses pattes.

Composition au lieu d'héritage

C'est un sujet très vaste et intéressant et il mérite au moins une conférence séparée. De nombreuses copies ont été cassées sur ce sujet sur Internet jusqu'à ce qu'un consensus soit atteint - nous utilisons l'héritage au minimum, la composition - au maximum.

Le fait est que l'héritage fournit en fait le lien le plus fort entre les classes, il doit donc être évité. Ce sujet est bien traité dans l'article de Herb Sutter " Prefer Composition Over Inheritance ".

Lorsque vous commencerez à apprendre les modèles de conception, vous rencontrerez tout un tas de modèles qui régissent la création d'un objet ou sa structure interne. Au passage, je peux conseiller dans ce cadre de faire attention au pattern Delegate/Delegate et au pattern Component , qui sont issus de games .

Nous parlerons davantage des motifs un peu plus tard.