Dans leur forme pure, les modèles sont assez rares, et lorsque l'on étudie les modèles, en particulier dans les premiers stades, ce ne sont pas tant les modèles eux-mêmes qui importent que la compréhension des mécanismes (tactiques) avec lesquels ils sont mis en œuvre. Dans cet article, je voudrais décrire l'un de ces mécanismes (gestion de la dépendance), qui est utilisé dans les modèles Observateur et Médiateur, mais qui est souvent négligé. Si vous commencez tout juste à apprendre des modèles, alors bienvenue au chat.
Gestion des dépendances
Commençons par la déclaration: si la classe A dépend de la classe B, alors, sans changer le comportement, vous pouvez réécrire le code pour que la classe B dépend de la classe A ou introduire une autre classe C pour que les classes A et B soient indépendantes, et la classe C se liera et dépendra des classes A et B.

Une classe dépend d'une autre si elle fait référence à ses champs ou méthodes. Les champs sont facilement transférés d'une classe à une autre, nous allons donc nous attarder sur les méthodes plus en détail. Supposons que la classe B contient la méthode Print, qui imprime la date actuelle sur la console, et que la classe A appelle cette méthode.
class A { private readonly B _b; public A(B b) { _b = b; } public void Run() { _b.Print(); } } class B { public void Print() { Console.WriteLine(DateTime.Now.ToString()); } } public void Test() { var b = new B(); var a = new A(b); a.Run(); }
Ainsi, la classe A dépend de la classe B. Pour inverser cette dépendance, au lieu d'appeler directement la méthode Print, nous allons générer un événement. Maintenant, la classe A ne sait rien de la classe B, et la classe B peut souscrire à un événement de la classe A. C'est-à-dire la classe B dépendra de la classe A.
class A { public event EventHandler PrintRequested; public void Run() { PrintRequested.Invoke(this, EventArgs.Empty); } } class B { private readonly A _a; public B(A a) { _a = a; _a.PrintRequested += (s, e) => Print(); } public void Print() { Console.WriteLine(DateTime.Now.ToString()); } } public void Test() { var a = new A(); var b = new B(a); a.Run(); }
Le comportement du code ne change pas et, dans la méthode appelante, seul l'ordre d'initialisation des objets et le transfert des dépendances au constructeur sont modifiés.
En fait, c'est l'implémentation du modèle
Observer en C #. La classe A est un observable et la classe B est un observateur. La classe A est une classe indépendante qui génère des notifications (événements). Les autres classes intéressées peuvent souscrire à ces événements et appliquer leur logique. Le système devient plus dynamique du fait que maintenant la classe A n'a plus besoin de connaître les autres implémentations. Nous pouvons ajouter de nouvelles implémentations qui s'abonneront aux événements, tandis que la classe A restera inchangée.
Vous pouvez aller plus loin et supprimer la dépendance de la classe B sur A en ajoutant du code externe qui reliera les deux classes, c'est-à-dire abonner un objet aux événements d'un autre.
class A { public event EventHandler PrintRequested; public void Run() { PrintRequested.Invoke(this, EventArgs.Empty); } } class B { public void Print() { Console.WriteLine(DateTime.Now.ToString()); } } class C { public void Test() { var a = new A(); var b = new B(); a.PrintRequested += (s, e) => b.Print(); a.Run(); } }
Maintenant, les classes A et B sont complètement indépendantes, chacune accomplit sa tâche et ne sait rien des autres classes. La logique de l'interaction entre les objets entre dans une nouvelle classe. Seule la classe C sait en réponse à quels événements et dans quelles conditions les méthodes de classe B doivent être appelées. Ainsi, la classe C devient un
médiateur .
Résumé: Combattre la complexité du système
Un des problèmes importants de la programmation est la présence de systèmes complexes issus de classes enchevêtrées avec un grand nombre de dépendances (systèmes étroitement couplés). En gérant les dépendances, vous pouvez réduire la connectivité, simplifier le système et atteindre une plus grande agilité et flexibilité.
Le modèle
Observer réduit la connectivité en inversant les dépendances. Il est bien applicable lorsqu'il existe plusieurs sources d'événements et de nombreux écouteurs ajoutés dynamiquement. Un autre bon exemple de l'utilisation de ce modèle est
la programmation réactive , lorsqu'un changement dans l'état d'un objet entraîne un changement dans l'état de tous les objets qui en dépendent, et ainsi de suite.

Le modèle de
médiateur réduit la connectivité du système car toutes les dépendances vont dans une seule classe, le médiateur et toutes les autres classes deviennent indépendants et ne sont responsables que de la logique qu'ils exécutent. Ainsi, l'ajout de nouvelles classes devient plus facile, mais avec chaque nouvelle classe, la logique de médiateur est très compliquée. Au fil du temps, si le médiateur continue de croître de façon incontrôlable, il devient alors très difficile à maintenir.

Un piège dangereux lors de l'utilisation des modèles Observateur et Médiateur est la présence de références circulaires, lorsque des événements de la même classe, traversant une chaîne d'objets, conduisent à nouveau à la génération du même événement. Ce problème est difficile à résoudre et complique considérablement l'utilisation des modèles.
Ainsi, dans différentes circonstances, en gérant les dépendances, vous pouvez arriver à différents modèles, parfois à un mélange d'entre eux, et parfois ce mécanisme sera utile sans utiliser de modèles du tout. Combattez la complexité et ne produisez pas d'entités.
