Gerenciamento de dependências, eventos e padrões de observadores e mediadores

Na sua forma pura, os padrões são bastante raros e, ao estudar padrões, especialmente nos estágios iniciais, não são tanto os próprios padrões que são importantes que a compreensão dos mecanismos (táticas) com os quais são implementados. Neste artigo, gostaria de descrever um desses mecanismos (gerenciamento de dependências), usado nos padrões Observador e Mediador, mas que geralmente é ignorado. Se você está apenas começando a aprender padrões, bem-vindo ao gato.

Gerenciamento de Dependências


Vamos começar com a instrução: se a classe A depende da classe B, então, sem alterar o comportamento, você pode reescrever o código para que a classe B dependa da classe A ou introduzir outra classe C para que as classes A e B sejam independentes e classe C ligará e dependerá das classes A e B.



Uma classe depende de outra se se referir a seus campos ou métodos. Os campos são facilmente transferidos de uma classe para outra, portanto, vamos nos aprofundar nos métodos com mais detalhes. Suponha que a classe B contenha o método Print, que imprime a data atual no console, e a classe A chama esse 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(); } 

Portanto, a classe A depende da classe B. Para reverter essa dependência, em vez de chamar o método Print diretamente, geraremos um evento. Agora, a classe A não sabe nada sobre a classe B, e a classe B pode se inscrever em um evento da classe A. classe B dependerá da 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(); } 

O comportamento do código não muda e, no método de chamada, apenas a ordem de inicialização dos objetos e a transferência de dependências para o construtor são alteradas.

De fato, essa é a implementação do padrão Observer em C #. A classe A é um observável e a classe B é um observador. A classe A é uma classe independente que gera notificações (eventos). Outras classes que estão interessadas nisso podem se inscrever nesses eventos e executar sua lógica. O sistema se torna mais dinâmico devido ao fato de que agora a classe A não precisa conhecer outras implementações. Podemos adicionar novas implementações que se inscreverão em eventos, enquanto a classe A permanecerá inalterada.

Você pode ir além e remover a dependência da classe B em A adicionando código externo que vinculará as duas classes, ou seja, inscreva um objeto nos eventos de outro.

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

Agora as classes A e B são completamente independentes, cada uma executa sua tarefa e não sabe nada sobre as outras classes. A lógica para a interação entre objetos entra em uma nova classe. Somente a classe C sabe em resposta a quais eventos e sob quais condições os métodos da classe B. devem ser chamados. Assim, a classe C se torna um mediador .

Resumo: Combatendo a complexidade do sistema


Um dos problemas importantes na programação é a presença de sistemas complexos de classes emaranhadas com um grande número de dependências (sistemas fortemente acoplados). Ao gerenciar dependências, você pode reduzir a conectividade, simplificar o sistema e obter maior agilidade e flexibilidade.

O padrão Observer reduz a conectividade revertendo dependências. É bem aplicável quando existem várias fontes de eventos e muitos ouvintes que são adicionados dinamicamente. Outro bom exemplo do uso desse padrão é a programação reativa , quando uma mudança no estado de um objeto leva a uma mudança no estado de todos os objetos dependentes dele, e assim por diante.



O padrão Mediador reduz a conexão do sistema devido ao fato de que todas as dependências entram em uma classe, o mediador e todas as outras classes se tornam independentes e são responsáveis ​​apenas pela lógica que executam. Assim, a adição de novas classes se torna mais fácil, mas a cada nova classe a lógica do mediador é muito complicada. Com o tempo, se o mediador continuar a crescer incontrolavelmente, torna-se muito difícil de manter.


Uma armadilha perigosa ao usar os padrões Observador e Mediador é a presença de referências circulares, quando eventos da mesma classe, passando por uma cadeia de objetos, levam à geração do mesmo evento novamente. Esse problema é difícil de resolver e complica significativamente o uso de padrões.

Assim, em diferentes circunstâncias, gerenciando dependências, é possível chegar a padrões diferentes, às vezes a uma mistura deles, e às vezes esse mecanismo será útil sem o uso de padrões. Lute contra a complexidade e não produza entidades.

imagem

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


All Articles