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

Funções de data Oracle

  Com o Oracle é possível obter uma série de resultados a partir de uma ou mais datas, como por exemplo verificar o último dia do mês ou verificar a quantidade de meses entre duas datas, então vamos a alguns exemplos:   Data atual do sistema: SYSDATE Remover meses de uma data: ADD_MONTHS(SYSDATE, -1) Adicionar meses de uma data: ADD_MONTHS(SYSDATE, +1) Buscar o último dia do mês: LAST_DAY(SYSDATE) Primeiro dia do mês: TRUNC(SYSDATE, ‘MONTH’) Quantidade de meses entre duas datas: MONTHS_BETWEEN(SYSDATE, ‘27/07/1982’) Primeiro dia do ano: TRUNC(SYSDATE, ‘YEAR’) Dias da semana: DECODE( TO_NUMBER( TO_CHAR          (SYSDATE, ‘D’) ) ,1, ‘domingo’ ,2, ‘segunda-feira’ ,3, ‘terça-feira’ ,4, ‘quarta-feira’ ,5, ‘quinta-feira’ ,6, ‘sexta-feira’ ,7,’sábado’ )

Funções de Data e Hora (Delphi)

É muito comum nos depararmos no dia a dia com a necessidade de manipular datas e horas, seja para um calculo de permanência, dias de atraso enfim, é praticamente escapar de alguma situação que necessite desse tipo de controle. Assim como a necessidade e se utilizar algum recurso para manipular as datas e horas de alguma maneira e freqüente, as duvidas de como o faze-lo também é, basta um breve olhar em qualquer fórum especializado e lá está, alguma duvida relacionada, por isso decidi falar um pouco sobre uma unit muito poderosa chamada DateUtils para a manipulação de data e hora, com um grande numero de métodos e classes que facilitam a vida de qualquer um. Alguns exemplos: CompareDate(constA, B: TDateTime): TValueRelationship; Compara apenas a data de dois valores (do tipo TDateTime) retornando: LessThanValue O primeiro valor é menor que o segundo EqualsValue Os valores são iguais GreaterThanValue O primeiro valor é maior que o segundo CompareDateTime(const A, B: TD

Como Verificar se um Objeto Existe (Delphi)

Em alguns momentos surge a necessidade de verificar se um determinado objeto existe, ou seja se já foi criado, principalmente quando se trabalha com criação dinâmica em tempo de execução, então vamos ao exemplo: - Vamos criar uma variável, um vetor do tipo caixa de texto: var Minha_caixa : array of TEdit; - Em seguida definir o tamanho desse vetor, no caso será dez: setLength(Minha_caixa, 10) - Agora iremos criar nossa caixa de texto: // lembrando que o vetor inicia em zero // logo o índice final é o tamanho total - 1 for vl_i := 0 to Length(Minha_caixa) -1 do begin Minha_caixa[vl_i] := TEdit.Create(self); with Minha_caixa[vl_i] do begin Parent := Self; Name := 'Caixa_N'+IntToStr(vl_i); Text := 'Esta é a '+IntToStr(vl_i)+' º caixa !'; ReadOnly := true; Height := 21; Width :=