O Padrão de Projeto Chain of Responsability, através de uma cadeia de responsabilidades, consegue evitar dependências entre um objeto receptor e um objeto solicitante. Ou seja, um objeto fica como responsável por decidir se vai processar alguma mensagem ou a passar a responsabilidade para o próximo objeto.
A intenção do padrão Chain of Responsability está bem clara. O objeto receptor, ao receber a solicitação do remetente, vai castateando entre outros objetos até que um saiba como tratar a solicitação. Enquanto um não atender a expectativa, vai descendo e descendo. Ou seja, o objeto tratador da solicitação não é especificado explicitamente.
Pense no seguinte problema. Imagine que você está modelando um sistema de vendas e tem um requisito de negócio que dependendo do valor da venda, precisará de uma autorização especial. Exemplo, se o calor for menor que 3 mil, o próprio vendedor pode aprovar. Entre 3 e 30 mil, precisa da aprovação do gerente. Caso o valor seja superior a este valor máximo, precisa ser aprovado pelo Diretor.
Vamos para os códigos que fica mais divertido colocar o Padrão Chain de Responsability em ação. Primeiro vamos definir o nosso contrato do método responsável pela aprovação da venda e do nosso Sale que será nosso Value Object.
<?php declare(strict_types=1); namespace DesignPattern\Behavioral\ChainOfResponsability; namespace Growthdev\DesignPatterns\Behavioral\ChainOfResponsability; interface SaleHandler { public function processSale(Sale $sale): void; }
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Behavioral\ChainOfResponsability; final class Sale { public readonly float $price; public function __construct(float $price) { $this->price = $price; } }
Para deixar este exemplo bem mais próximo do diagrama UML original, modelei apenas adicionando a interface Sale handler como contrato para processar as vendas. De resto, você consegue perceber o auto-relacionamento de recursividade da classe abstrata ApproveHandler
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Behavioral\ChainOfResponsability; abstract class ApproveHandler implements SaleHandler { private ?ApproveHandler $nextHandler = null; public final function setNext(ApproveHandler $nextHandler): ApproveHandler { $this->nextHandler = $nextHandler; return $nextHandler; } public function processSale(Sale $sale): void { if ($this->nextHandler) { $this->nextHandler->processSale($sale); } } }
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Behavioral\ChainOfResponsability; // vendedor final class SellerHandler extends ApproveHandler { public function processSale(Sale $sale): void { if ($sale->price < 3_000) { printf("Sale approved by seller with price %.2f\n", $sale->price); } else { parent::processSale($sale); } } }
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Behavioral\ChainOfResponsability; // gerente final class ManagerHandler extends ApproveHandler { public function processSale(Sale $sale): void { if ($sale->price >= 3_000 && $sale->price < 30_000) { printf("Sale approved by manager with price %.2f\n", $sale->price); } else { parent::processSale($sale); } } }
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Behavioral\ChainOfResponsability; // Diretor final class DirectorHandler extends ApproveHandler { public function processSale(Sale $sale): void { if ($sale->price >= 30_000) { printf("Sale approved by director with price %.2f\n", $sale->price); } else { parent::processSale($sale); } } }
Veja na implementação dos testes, como o padrão Chain of Responsibility deixa nosso código limpo e muito organizado. Obviamente, quando você memoriza o comportamento deste padrão, você vai ver que muitos frameworks web utilizam largamente este pattern. Se você souber de algum exemplo, deixe aqui nos comentários.
<?php declare(strict_types=1); namespace Growthdev\DesignPatterns\Tests\Behavioral\ChainOfResponsability; use Growthdev\DesignPatterns\Behavioral\ChainOfResponsability\DirectorHandler; use Growthdev\DesignPatterns\Behavioral\ChainOfResponsability\ManagerHandler; use Growthdev\DesignPatterns\Behavioral\ChainOfResponsability\Sale; use Growthdev\DesignPatterns\Behavioral\ChainOfResponsability\SellerHandler; use PHPUnit\Framework\TestCase; final class ChainOfResponsabilityTest extends TestCase { public function testExpectOfApproveBySeller() { $seller = new SellerHandler; $seller->setNext(new ManagerHandler) ->setNext(new DirectorHandler); // Request $sale = new Sale(2_999.99); $seller->processSale($sale); $this->expectOutputString("Sale approved by seller with price 2999.99\n"); } public function testExpectOfApproveByManager() { $seller = new SellerHandler; $seller->setNext(new ManagerHandler) ->setNext(new DirectorHandler); // Request $sale = new Sale(3_999.99); $seller->processSale($sale); $this->expectOutputString("Sale approved by manager with price 3999.99\n"); } public function testExpectOfApproveByDirector() { $seller = new SellerHandler; $seller->setNext(new ManagerHandler) ->setNext(new DirectorHandler); // Request $sale = new Sale(30_000.01); $seller->processSale($sale); $this->expectOutputString("Sale approved by director with price 30000.01\n"); } }
Se você ainda não está acompanhando, este artigo é mais de da série de resumo dos padrões de projetos. Você também pode encontrar todos os códigos de todos os exemplos no meu Github:
https://github.com/growthdev-repo/design-patterns
Se você estiver gostando desta série e quiser ajudar, basta compartilhar com seus amigos e colegas de trabalho para que estes artigos cheguem ao máximo de pessoas possíveis. Até o próximo artigo!
Confiança Sempre!!!
Fontes:
Seja o primeiro a comentar