Gestión de dependencias, eventos y patrones de observadores y mediadores

En su forma pura, los patrones son bastante raros, y cuando se estudian los patrones, especialmente en las primeras etapas, no son tanto los patrones en sí mismos los que son importantes como la comprensión de los mecanismos (tácticas) con los que se implementan. En este artículo, me gustaría describir uno de estos mecanismos (gestión de dependencia), que se utiliza en los patrones de Observador y Mediador, pero que a menudo se pasa por alto. Si recién estás comenzando a aprender patrones, entonces bienvenido a cat.

Gestión de dependencias


Comenzamos con la declaración: si la clase A depende de la clase B, entonces, sin cambiar el comportamiento, puede reescribir el código para que la clase B dependa de la clase A o introducir otra clase C para que las clases A y B sean independientes, y la clase C se unirá y dependerá de las clases A y B.



Una clase depende de otra si se refiere a sus campos o métodos. Los campos se transfieren fácilmente de una clase a otra, por lo que nos detendremos en los métodos con más detalle. Supongamos que la clase B contiene el método Print, que imprime la fecha actual en la consola, y la clase A llama a este método.

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(); } 

Por lo tanto, la clase A depende de la clase B. Para revertir esta dependencia, en lugar de llamar directamente al método Print, generaremos un evento. Ahora la clase A no sabe nada sobre la clase B, y la clase B puede suscribirse a un evento de la clase A. la clase B dependerá de la clase 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(); } 

El comportamiento del código no cambia, y en el método de llamada solo se cambia el orden de inicialización de los objetos y la transferencia de dependencias al constructor.

De hecho, esta es la implementación del patrón Observador en C #. La clase A es un observable, y la clase B es un observador. La clase A es una clase independiente que genera notificaciones (eventos). Otras clases interesadas en esto pueden suscribirse a estos eventos y llevar a cabo su lógica. El sistema se vuelve más dinámico debido al hecho de que ahora la clase A no necesita conocer otras implementaciones. Podemos agregar nuevas implementaciones que se suscribirán a eventos, mientras que la clase A permanecerá sin cambios.

Puede ir más allá y eliminar la dependencia de la clase B en A agregando código externo que vinculará las dos clases, es decir. suscriba un objeto a los eventos de otro.

 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(); } } 

Ahora las clases A y B son completamente independientes, cada una realiza su tarea y no sabe nada sobre las otras clases. La lógica para la interacción entre objetos entra en una nueva clase. Solo la clase C sabe en respuesta a qué eventos y bajo qué condiciones deberían llamarse los métodos de clase B. Por lo tanto, la clase C se convierte en un mediador .

Resumen: complejidad del sistema de lucha


Uno de los problemas importantes en la programación es la presencia de sistemas complejos de clases enredadas con una gran cantidad de dependencias (sistemas estrechamente acoplados). Al administrar las dependencias, puede reducir la conectividad, simplificar el sistema y lograr una mayor agilidad y flexibilidad.

El patrón Observador reduce la conectividad al revertir las dependencias. Es bien aplicable cuando hay varias fuentes de eventos y muchos oyentes que se agregan dinámicamente. Otro buen ejemplo del uso de este patrón es la programación reactiva , cuando un cambio en el estado de un objeto conduce a un cambio en el estado de todos los objetos que dependen de él, y así sucesivamente.



El patrón de mediador reduce la conectividad del sistema debido al hecho de que todas las dependencias entran en una clase, el mediador y todas las demás clases se vuelven independientes y son responsables solo de la lógica que ejecutan. Por lo tanto, la adición de nuevas clases se vuelve más fácil, pero con cada nueva clase la lógica del mediador es muy complicada. Con el tiempo, si el mediador continúa creciendo incontrolablemente, se vuelve muy difícil de mantener.


Una trampa peligrosa cuando se usan los patrones de Observador y Mediador es la presencia de referencias circulares, cuando los eventos de una clase, que pasan a través de una cadena de objetos, conducen a la generación del mismo evento nuevamente. Este problema es difícil de resolver y complica significativamente el uso de patrones.

Por lo tanto, en diferentes circunstancias, al administrar dependencias, puede llegar a diferentes patrones, a veces a una mezcla de ellos, y a veces este mecanismo será útil sin usar patrones en absoluto. Lucha contra la complejidad y no produce entidades.

imagen

Source: https://habr.com/ru/post/es420783/


All Articles