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

Aplicação Flask usando Nginx e Gunicorn

Aplicação Flask usando Nginx e Gunicorn Se você já desenvolveu uma aplicação Flask básica, sabe que o servidor de desenvolvimento embutido não é ideal para produção. Ele não é projetado para lidar com altos volumes de tráfego ou conexões simultâneas. Para tornar sua aplicação Flask mais robusta e pronta para produção, podemos usar o Gunicorn como servidor de aplicação e o Nginx como proxy reverso. Neste artigo, vamos adaptar o exemplo anterior ( Criando uma Aplicação CRUD com Flask, PostgreSQL e Docker ) para incluir o Nginx e o Gunicorn. O que são Nginx e Gunicorn? Gunicorn O Gunicorn (Green Unicorn) é um servidor de aplicação WSGI que roda aplicações Python como o Flask. Ele é eficiente e simples de configurar, lidando com múltiplas requisições ao mesmo tempo, algo que o servidor embutido do Flask não faz bem. Nginx O Nginx é um servidor web que atua como um proxy reverso. Ele recebe requisições HTTP e as encaminha ao Gunicorn. Além disso, o Nginx pode: Servir arquivos ...

Criando sua Primeira Aplicação Web com Python e Docker

  Criando sua Primeira Aplicação Web com Python e Docker Hoje vamos fazer algo prático: criar uma aplicação web simples em Python usando Flask e executá-la dentro de um contêiner Docker. Se você não sabe exatamente o que é Docker ou Flask, não se preocupe! Vou explicar tudo de forma simples para que você consiga acompanhar sem problemas. O que é Docker? Docker é uma ferramenta que permite empacotar aplicações e suas dependências em contêineres, garantindo que elas funcionem da mesma forma em qualquer ambiente. Em outras palavras, com o Docker, você cria um ambiente isolado e padronizado para rodar suas aplicações. Como instalar o Docker Acesse o site oficial do Docker . Baixe e instale a versão apropriada para o seu sistema operacional (Windows, macOS ou Linux). Após a instalação, verifique se está tudo funcionando abrindo um terminal e executando: docker --version Se o Docker estiver instalado corretamente, ele vai exibir a versão instalada. O que é Flask? Flask é um m...

Criando uma Aplicação CRUD com Flask, PostgreSQL e Docker

Criando uma Aplicação CRUD com Flask, PostgreSQL e Docker Neste guia, vamos criar uma aplicação básica que acessa um banco de dados PostgreSQL e realiza operações CRUD (Create, Read, Update, Delete). Vamos usar Flask e executar tudo com Docker. Sem estilos ou extras, apenas o essencial. Estrutura do Projeto crud-app/ |-- app/ | |-- app.py | |-- templates/ | | |-- index.html | | |-- edit.html |-- Dockerfile |-- requirements.txt |-- docker-compose.yml Passo 1: Dependências Crie um arquivo requirements.txt com as seguintes linhas: Flask==2.2.2 Flask-SQLAlchemy==3.0.2 psycopg2-binary==2.9.3 Werkzeug==2.2.2 Passo 2: Aplicação Flask Arquivo app/app.py : from flask import Flask, render_template, request, redirect, url_for from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) # Configuração do banco de dados app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://user:password@db:5432/crud_db' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db...