Pular para o conteúdo principal

C# - Design Patterns - Chain of Responsibility

Olá a  todos! Seguindo a proposta de padrões de projeto com C#, falaremos hoje sobre o padrão comportamental Chain of Responsibility. Para ver outros padrões que já visitamos por aqui consulte esse link. Esse tema foi tratado de forma brilhante aqui.


Chain of Responsibility


O padrão de projeto Chain of Responsibility é um padrão de design comportamental que permite passar uma solicitação ao longo de uma cadeia de objetos até que um deles a trate. Cada objeto na cadeia tem a chance de tratar a solicitação. É útil quando há múltiplos objetos que podem responder a uma solicitação e você não sabe antecipadamente qual objeto será o responsável.


No C#, a implementação deste padrão envolve a definição de uma interface para as solicitações, criação de classes concretas para tratar as solicitações e definição de uma lógica de encadeamento entre essas classes. Ao receber uma solicitação, cada objeto na cadeia verifica se pode tratá-la e, se não puder, passa a solicitação para o próximo objeto na cadeia.


Quando podemos usar?


O Chain of Responsibility deve ser usado em situações onde:

  • Existe múltiplos objetos que podem responder a uma solicitação e você não sabe antecipadamente qual objeto será o responsável.
  • Você quer evitar um acoplamento rígido entre as solicitações e seus tratadores, permitindo que o número e o tipo de tratadores sejam especificados dinamicamente.
  • Você quer evitar o envio de uma solicitação a objetos desnecessários, tornando o processo de tratamento mais eficiente.
  • Você deseja que vários objetos tenham a chance de tratar a solicitação, sem que seja necessário especificar explicitamente a ordem em que os objetos serão verificados.

Além disso, o Chain of Responsibility é especialmente útil quando existe múltiplos tratadores que podem ser adicionados ou removidos ao longo do tempo, sem que seja necessário alterar a classe que envia as solicitações.


Exemplo


No exemplo abaixo, a interface IRequest representa as solicitações e é implementada pela classe concreta PurchaseRequest. Esta classe representa uma solicitação de compra.


As três classes concretas de tratadores Director, VicePresident e President representam três níveis de aprovação para a solicitação de compra. Cada classe herda da classe abstrata Approver e implementa o método ProcessRequest para tratar a solicitação. O método verifica se o valor da solicitação é menor do que o seu limite de aprovação e, se for, aprova a solicitação. Se não puder aprovar a solicitação, ele passa para o próximo tratador na cadeia, se houver.


Na classe Program, criamos a cadeia de tratadores, definindo qual tratador é o sucessor de outro tratador na cadeia. Em seguida, enviamos três solicitações de compra diferentes para a cadeia, onde são processadas pelos tratadores na ordem correta, até que seja encontrado o tratador responsável ou a cadeia chegue ao fim.

    
        using System;

        // Define a interface para as solicitações
        interface IRequest
        {
            int Amount { get; set; }
        }
        
        // Define a classe concreta para as solicitações
        class PurchaseRequest : IRequest
        {
            public int Amount { get; set; }
            public PurchaseRequest(int amount)
            {
                this.Amount = amount;
            }
        }
        
        // Define a classe abstrata para os tratadores
        abstract class Approver
        {
            protected Approver successor;
            public void SetSuccessor(Approver successor)
            {
                this.successor = successor;
            }
            public abstract void ProcessRequest(IRequest request);
        }
        
        // Define as classes concretas para os tratadores
        class Director : Approver
        {
            public override void ProcessRequest(IRequest request)
            {
                if (request.Amount < 10000)
                {
                    Console.WriteLine("A solicitação de compra de R$" + request.Amount + " foi aprovada pelo Diretor.");
                }
                else if (successor != null)
                {
                    successor.ProcessRequest(request);
                }
            }
        }
        
        class VicePresident : Approver
        {
            public override void ProcessRequest(IRequest request)
            {
                if (request.Amount < 25000)
                {
                    Console.WriteLine("A solicitação de compra de R$" + request.Amount + " foi aprovada pelo Vice-Presidente.");
                }
                else if (successor != null)
                {
                    successor.ProcessRequest(request);
                }
            }
        }
        
        class President : Approver
        {
            public override void ProcessRequest(IRequest request)
            {
                if (request.Amount >= 25000)
                {
                    Console.WriteLine("A solicitação de compra de R$" + request.Amount + " foi aprovada pelo Presidente.");
                }
            }
        }
        
        class Program
        {
            static void Main(string[] args)
            {
                // Cria a cadeia de tratadores
                Approver director = new Director();
                Approver vicePresident = new VicePresident();
                Approver president = new President();
        
                director.SetSuccessor(vicePresident);
                vicePresident.SetSuccessor(president);
        
                // Envia as solicitações
                IRequest request = new PurchaseRequest(2000);
                director.ProcessRequest(request);
        
                request = new PurchaseRequest(20000);
                director.ProcessRequest(request);
        
                request = new PurchaseRequest(50000);
                director.ProcessRequest(request);
        
                Console.ReadKey();
            }
        }        
    

Conclusão

Como podemos perceber o padrão que abordamos aqui permite que vários objetos tentem responder a uma solicitação sem que a classe que envia essa solicitação saiba qual objeto é o responsável, criando uma cadeia de tratadores, onde cada tratador tem a oportunidade de processar a solicitação e passá-la para o próximo tratador na cadeia, se não puder tratá-la.

Comentários

Mais visitadas

Desabilitar POST do ENTER ASP .NET

Caso não seja necessário uma atualização de página a cada vez que o usuário pressionar a tecla “ ENTER ” podemos codificar uma pequena função em javascript anulando esse comportamento, segue o código: < script language ="javascript"> function desabilitaPostDoEnter(e) {      var key;      if (window.event)           key = window.event.keyCode;  //IE      else           key = e.which;                      //firefox      if (key == 13)           return false ;      else           return true ; } </ script > Agora nos objetos que se deseja c...

Lista de políticos com ficha suja

ATUALIZAÇÃO (08/03/2012 ano de eleição) Representantes de duas pessoas da lista (Eliseu Padilha e Alex Canziani) entraram em contato e pediram que esses fossem removidos, alegando que não houve condenação. É justo essa requisição, porém vale lembrar que escândalos nacionalmente reconhecidos de corrupção não deram em nada, por isso o que realmente conta nesse ano de eleição é uma pesquisa minuciosa sobre os candidatos escolhidos, eu particularmente, por não estar nem um pouco satisfeito, não irei votar em ninguém que já tenha sido eleito. Estou voltando a postar por um motivo nobre, meu tempo continua apertado mas esse post é rápido, na verdade nem meu ele é (visitem o espaço de nosso amigo Lord ), estou apenas repassando essa valiosa informação. Teremos eleição esse ano, e é importante não repetirmos erros passados, vamos ficar atentos em relação a esses nomes e exclui-los de vez do cenário politico nacional. Façamos nossa parte, publicando em nossos blog...

Ler de um arquivo de configuração (.INI no Delphi)

Em muitos casos precisamos tornar nossas aplicações customizáveis, disponibilizando parâmetros que possam ser alterados, sejam eles informações a respeito de uma conexão com o banco de dados, uma senha, ou mesmo possibilitar a alteração do layout do sistema, tudo depende da necessidade e de como o programador pensa em termos de mobilidade para o usuário final. Exemplo de arquivo INI [PARAMETRO] SENHA=12345 Lendo um arquivo INI var // variável que irá conter o arquivo config : TIniFile; // variável que irá armazenar parâmetro a ser lido do arquivo param : string; begin // associa o arquivo de configuração a variável config := TIniFile.Create('C:\Aplicacao\config.ini'); //variável param recebe 12345 param := config.readstring('PARAMETRO','SENHA',''); end;