Solucionado (ver solução)
Solucionado
(ver solução)
6
respostas

Sobre implementação

Realizei uma implementação do exercício de "requisições" do padrão Chain of responsibility usando uma classe abstrata.

Logo me ocorreu de saber a opinião de vocês sobre tal implementação.

E nesse tipo de implementação em específico, me parece ter um template method formando um chain.

O que acham, estou errado, meio certo, certo?

<?php


class Conta
{
    public $saldo = 0.0;
    public $titular = "";

    public function __construct($saldo, $titular)
    {
        $this->saldo = $saldo;
        $this->titular = $titular;
    }
}

class Formato 
{
    public static $XML = "XML";
    public static $CSV = "CSV";
    public static $PORCENTO = "PORCENTO";
}

class Requisicao 
{
      private $formato;

      public function __construct($formato)
      {
          $this->formato = $formato;
      }

      public  function getFormato()
      {
          return $this->formato;
      }
}

abstract class Resposta
{
    protected $tipoResposta;    
    public $proxima;

    public function enviarResposta(Conta $conta, Requisicao $requisicao)
    {
        if($requisicao->getFormato() != $this->tipoResposta && $this->proxima != NULL)
            return $this->proxima->enviarResposta($conta, $requisicao);

        return $this->responder($conta, $requisicao);
    }
    public function setProxima(Resposta $resposta)
    {
        return $this->proxima = $resposta;
    }
    protected abstract function responder(Conta $conta, Requisicao $requisicao);
}

class RespostaXml extends Resposta
{
    public $tipoResposta = "XML";

    public function responder(Conta $conta, Requisicao $requisicao)
    {
        $resposta = "";
        $resposta .= "<conta>";
        $resposta .= "<titular>";
        $resposta .= $conta->titular;
        $resposta .= "</titular>";
        $resposta .= "<saldo>";
        $resposta .= $conta->saldo;
        $resposta .= "</saldo>";
        $resposta .= "</conta>";
        return $resposta;
    }    
}

class RespostaCsv extends Resposta
{
    public $tipoResposta = "CSV";

    public function responder(Conta $conta, Requisicao $requisicao)
    {
        $resposta = "";
        $resposta .= $conta->titular;
        $resposta .= ";";
        $resposta .= $conta->saldo;
        $resposta .= ";";
        return $resposta;
    }
}

class RespostaPorcento extends Resposta
{
    public $tipoResposta = "PORCENTO";

    public function responder(Conta $conta, Requisicao $requisicao)
    {
        $resposta = "";
        $resposta .= $conta->titular;
        $resposta .= "%";
        $resposta .= $conta->saldo;
        $resposta .= "%";
        return $resposta;
    }
}

class RespostaSemFormato extends Resposta
{
    public $proxima = NULL;
    public function responder(Conta $conta, Requisicao $requisicao)
    {
        $resposta = "";
        return $resposta;
    }
}

class RespondeRequisicao
{
    public function resposta(Requisicao $requisicao, Conta $conta)
    {
        $respostaXml = new RespostaXml();
        $respostaCsv = new RespostaCsv();
        $respostaPorcento = new RespostaPorcento();
        $semResposta = new RespostaSemFormato();

        $respostaXml->setProxima($respostaCsv);
        $respostaCsv->setProxima($respostaPorcento);
        $respostaPorcento->setProxima($semResposta);

        return $respostaXml->enviarResposta($conta, $requisicao);
    }
}

$formato = Formato::$PORCENTO;
$conta = new Conta(500.00, "Eduardo Cesar");

$requisicao = new Requisicao($formato);

$responder = new RespondeRequisicao();

var_dump($responder->resposta($requisicao, $conta));
6 respostas

Oi Eduardo,

A implementação me parece ok, sim! Mas onde vc viu um Template Method? Qual é o método que tem o template?

Um abraço!

A primeiro momento o que eu havia imaginado foi nesses 2 métodos aqui :

 public function enviarResposta(Conta $conta, Requisicao $requisicao)
    {
        if($requisicao->getFormato() != $this->tipoResposta && $this->proxima != NULL)
            return $this->proxima->enviarResposta($conta, $requisicao);

        return $this->responder($conta, $requisicao);
    }
    public function setProxima(Resposta $resposta)
    {
        return $this->proxima = $resposta;
    }

Por que eles não seriam ?

solução!

Oi Eduardo,

Pq essa é a implementação mínima que vc precisa pra fazer o Chain of Responsibility funcionar.

Obviamente a implementação é parecida. No entanto, sempre que pensar num padrão de projeto, lembre-se que a motivação é fundamental. O template precisa ser um "senhor template", e não apenas algo simples como esse.

Percebeu a diferença semântica?

Um abraço!

Se estou entendendo, há um equivoco de minha parte em enxergar tal implementação como sendo um Template Method. Mas isso se da apenas pela inteção dos métodos, o fato deles serem pertinentes as particularidades de um chain ?

A implementação em si pode ser válida do ponto de vista do Chain ?

Exato, Eduardo. Você precisa ver primeiro o contexto e a motivação, para decidir qual padrão aplicar. E não só a implementação.

É um Chain sim! :)

Valeu Mauricio, grato pela conversa.

Forte abraço.