[sw design pattern] Chain of Responsability (cadeia de responsabilidade)
Os padrões de design GoF são classificados em três categorias: Criacionais, Comportamentais e Estruturais. Os padrões criacionais tratam da criação de objetos, enquanto os padrões estruturais lidam com a estrutura das classes, como herança e composição. Por fim, os padrões comportamentais lidam com a comunicação entre objetos e suas interações. Estes padrões de projeto se encontram no livro “Padrões de Projetos: Soluções Reutilizáveis de Software Orientado a Objetos” escrito por 4 autores denominados GoF (Gang of Four).
O padrão de projeto Chain of Responsibility (Cadeia de Responsabilidade) é um dos padrões mais populares criados pelo GoF. Este padrão pertence à categoria de padrões comportamentais, ou seja, ele lida com comportamentos entre objetos.
Resumidamente, o padrão de Cadeia de Responsabilidade é utilizado quando queremos dar a vários objetos a oportunidade de tratar uma solicitação. Esses objetos são organizados em uma cadeia e, quando uma solicitação é feita, ela é passada pela cadeia até que um objeto a trate.
Se o objeto inicial não puder lidar com a solicitação, ele passa a solicitação para o próximo objeto da cadeia, e assim por diante, até que a solicitação seja tratada ou até que a cadeia seja percorrida completamente e a solicitação não seja tratada.
Para um exemplo prático, considere um sistema de atendimento ao cliente em que há vários níveis de atendimento. Quando um cliente entra em contato, o primeiro nível de atendimento tenta resolver o problema. Se o problema for muito complexo, ele é passado para o próximo nível de atendimento, e assim por diante, até que o problema seja resolvido.
Aqui está um exemplo simples de código em Java que ilustra o uso do padrão Chain of Responsibility:
// Definimos uma interface para as classes que irão lidar com as solicitações
interface Handler {
public void setNext(Handler handler);
public void handleRequest(int request);
}
// Classe base que implementa a interface Handler
abstract class BaseHandler implements Handler {
private Handler nextHandler;
public void setNext(Handler handler) {
nextHandler = handler;
}
public void handleRequest(int request) {
if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
// Implementação concreta do Handler para lidar
// com solicitações menores que 50
class ConcreteHandler1 extends BaseHandler {
public void handleRequest(int request) {
if (request < 50) {
System.out.println("ConcreteHandler1 lida com a solicitação: " + request);
} else {
super.handleRequest(request);
}
}
}
// Implementação concreta do Handler
// para lidar com solicitações entre 50 e 100
class ConcreteHandler2 extends BaseHandler {
public void handleRequest(int request) {
if (request >= 50 && request < 100) {
System.out.println("ConcreteHandler2 lida com a solicitação: " + request);
} else {
super.handleRequest(request);
}
}
}
// Implementação concreta do Handler
// para lidar com solicitações maiores que 100
class ConcreteHandler3 extends BaseHandler {
public void handleRequest(int request) {
if (request >= 100) {
System.out.println("ConcreteHandler3 lida com a solicitação: " + request);
} else {
super.handleRequest(request);
}
}
}
// Classe que cria a cadeia de responsabilidade
class ChainOfResponsibilityExample {
public static void main(String[] args) {
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
Handler handler3 = new ConcreteHandler3();
handler1.setNext(handler2);
handler2.setNext(handler3);
// Solicitações são feitas à cadeia
handler1.handleRequest(25);
handler1.handleRequest(75);
handler1.handleRequest(125);
}
}
Neste exemplo, temos a classe abstrata BaseHandler
que implementa a interface Handler
. As classes ConcreteHandler1
, ConcreteHandler2
e ConcreteHandler3
são implementações concretas dessa classe abstrata. A classe ChainOfResponsibilityExample
cria a cadeia de responsabilidade e faz solicitações à cadeia.
Ao executar este exemplo, você deve ver a seguinte saída:
ConcreteHandler1 lida com a solicitação: 25
ConcreteHandler2 lida com a solicitação: 75
ConcreteHandler3 lida com a solicitação: 125
Existe algumas relações com outros padrões de design, a seguir descritas:
** Composite x Chain of Responsability:
O padrão Composite é um padrão estrutural que permite que objetos sejam agrupados em uma hierarquia de árvore, e permite que o cliente trate objetos individuais e composições de objetos de maneira uniforme. O padrão Chain of Responsibility, por outro lado, é um padrão comportamental que permite que uma série de objetos tratadores processem uma solicitação, com cada objeto na cadeia decidindo se processará a solicitação ou passará para o próximo objeto na cadeia.
Embora esses padrões tenham finalidades diferentes, pode haver situações em que eles são usados juntos. Por exemplo, em uma hierarquia de Composite, uma solicitação pode ser passada de um objeto composto para seus componentes folha por meio de uma cadeia de objetos Chain of Responsibility.
Em resumo, o padrão Composite é usado para compor objetos em uma hierarquia de árvore, enquanto o Chain of Responsibility é usado para tratar solicitações em uma cadeia de objetos tratadores.
** Decorator x Chain of Responsability:
Apesar de os padrões Chain of Responsibility e Decorator apresentarem semelhanças, como a capacidade de manipular um objeto antes de passá-lo adiante, eles diferem em que o padrão Chain of Responsibility pode interromper o processamento em qualquer ponto da cadeia, enquanto o Decorator executa todas as etapas.
links externos:
*** O artigo abaixo também descreve uma implementação em Java do padrão Chain of Responsibility usando um exemplo de cenário onde um banco precisa processar empréstimos com base no tipo de empréstimo e no valor solicitado. O autor enfatiza que o padrão é útil quando há um grupo de objetos que podem lidar com uma solicitação, mas é desconhecido qual objeto específico irá tratá-la.
https://medium.com/xp-inc/design-patterns-parte-15-chain-of-resposability-8790ebb5d443
*** No exemplo fornecido no artigo a seguir, a classe Chain
é responsável por construir e manter a cadeia de processadores de solicitações, representados pelas classes NegativeProcessor
, ZeroProcessor
e PositiveProcessor
. Cada processador lida com uma condição específica da solicitação e, se não puder lidar com ela, passa a solicitação para o próximo processador na cadeia, através do método process
implementado na classe abstrata Processor
. O método main
da classe TestChain
cria uma instância de Chain
e, em seguida, chama o método process
com quatro objetos da classe Number
como argumentos, cada um com um valor diferente. O resultado é a impressão das mensagens na saída padrão, correspondentes às condições atendidas pelos processadores.
https://www.geeksforgeeks.org/chain-responsibility-design-pattern/
*** O código descrito no artigo abaixo contém uma classe abstrata chamada AbstractLogger
que define três níveis de logging (INFO, DEBUG e ERROR) e um método abstrato chamado write()
. Há três classes concretas que herdam da classe AbstractLogger
: ConsoleLogger
, ErrorLogger
e FileLogger
, cada uma com um nível de logging específico e uma implementação para o método write().
A classe ChainPatternDemo
possui um método estático getChainOfLoggers()
que define a ordem em que as solicitações serão processadas pelas classes concretas. No método main()
, a variável loggerChain
é inicializada com essa cadeia de responsabilidade, e três mensagens são logadas com níveis diferentes para demonstrar como a cadeia de responsabilidade funciona.
https://www.tutorialspoint.com/design_pattern/chain_of_responsibility_pattern.htm
REFERÊNCIAS:
https://refactoring.guru/design-patterns/chain-of-responsibility
https://www.geeksforgeeks.org/chain-responsibility-design-pattern/
https://www.pmi.org/disciplined-agile/the-design-patterns-repository/the-chain-of-responsibility-pattern
https://angrynerds.co/blog/chain-of-responsibility-design-pattern-with-examples/
https://softwareengineering.stackexchange.com/questions/445204/what-is-the-difference-between-c-composite-and-chain-of-responsibility-pattern