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

PHP - Design Pattern I: Fim da corrente no Chain of Responsibility

Opinião do Instrutor no exercicio é que podemos fazer com que o próximo ítem da corrente seja opcional (ou seja, o atributo outraResposta fica com null caso não haja um próximo ítem). E, no momento de invocar o próximo, você deve verificar se esse atributo é nulo ou não.

Contudo no exemplo que ele dá, o proximo item é obrigatório quando você está instanciando um objeto:

class RespostaEmPorcento implements Resposta {
    private $outraReposta;

    function __construct(Resposta $outraResposta) {
        $this->outraResposta = $outraResposta;
    }

    public function RespostaEmPorcento() {
        $this->outraResposta = null; // nao recebi a proxima!
    }

     public function responde(Requisicao $req, Conta $conta) {
        if($req->getFormato() == Formato::PORCENTO) {
            echo $conta->getTitular() . '%' . $conta->getSaldo();
        } else if(!is_null($outraResposta)){
            $outraResposta->responde($req, $conta);
        } else {
            // não existe próxima na corrente, e ninguém atendeu a requisição!
            // poderíamos não ter feito nada aqui, caso não fosse necessário!
            throw new Exception("Formato de resposta não encontrado");
        }
    }
}

Notem que no construtor é esperado um objeto do tipo Resposta. Neste caso não seria interessante criar uma nova classe apenas para tratar este erro?

class RespostaNaoEncontrada implements Resposta {

  public function __construct(){}

  public function responde(Requisicao $req, Conta $conta)
  {
      throw new Exception("Formato de resposta inexistente");
  }
}
4 respostas

Oi Hedcler, na real eu acho melhor remover o parametro do construtor para ficar algo como:

function __construct(Resposta $outraResposta) {
        $this->outraResposta = null;
    }

Assim como a classe é a ponta da cadeia ela não pode receber uma próxima resposta.

A ideia de criar uma outra classe que ao chamar o método responde lança uma exception é um pouco perigosa afinal a única coisa que a classe faz é lançar uma exceção e não representa mais nada alem disso. Eu manteria a exceção na classe RespostaEmPorcento mesmo assim eu evito que alguém crie um objeto que só lança exception.

Espero ter ajudado. Abraços.

Oi Renan,

Neste caso a injeção de dependência no construtor deveria ser opcional certo? Algo parecido com isto:

function __construct(Resposta $outraResposta = null) {
        $this->outraResposta = null;
}

para implementar a interface e evitar o seguinte erro:

Argument 1 passed to <Object>::__construct() must be an instance of <Dependency>

correto?

Testei na CLI:

php > interface A { function __construct(Dependency $dep); }
php > class ObjA implements A { function __construct(Dependency $dep = null){ $this->dep = null; }  }
php > new ObjA();
php > class ObjB implements A { function __construct(Dependency $dep){ $this->dep = null; }  }
php > new ObjB();
PHP Catchable fatal error:  Argument 1 passed to ObjB::__construct() must be an instance of Dependency, none given, called in php shell code on line 1 and defined in php shell code on line 1
PHP Stack trace:
PHP   1. {main}() php shell code:0
PHP   2. {main}() php shell code:0
PHP   3. Teste2->__construct() php shell code:1
PHP   4. {main}() php shell code:0
PHP   5. ObjB->__construct() php shell code:1
php >

Ou existe outra forma de evitar o erro?

solução!

Isso é um detalhe bem zuado do PHP, se você tipa o parâmetro ele para de aceitar null a menos que você fale que aceite null (a partir do php 7.1)

function __construct(?Resposta $outraResposta) {
        $this->outraResposta = null;
}

Perfeito. Muito obrigado Renan!