6
respostas

Combinando o Decorator e Chain of Responsability

Entendi a diferença entre o Decorator e Chain of Responsability. No primeiro sabemos quais situações queremos acumular para que sejam processadas as possibilidades, no segundo, entregamos o problema para uma cadeia de possibilidades, e nesta cadeia, o primeiro que resolver o problema é chamado. Contudo minha dúvida é a seguinte: e se eu preciso de um Chain of Responsability em que o problema é atendido por mais de uma situação da Cadeia? Eu tentei codificar aqui e funcionou, mas queria saber se isso é um outro padrão, ou se já tem alguma solução elegante para isso?

6 respostas

Acho que eu precisava entender mais da situação para ter uma opinião melhor.. Dado o que vc falou, para mim parece, que talvez, vc tenha divido muito a responsabilidade e aí acabou com duas classes que podem tratar do seu problema.

Vamos pegar o exemplo da aula de Chain of Responsability, que são os impostos, ou seja, se o caso concreto cair na condição do imposto X, então o desconto do imposto X é aplicado. Meu problema ocorre quando eu preciso aplicar a condição do imposto X e do imposto Y combinadas. Na implementação da aula do Chain of Responsability, isso não é possível, uma vez que a condição do imposto X é satisfeita, será aplicado o desconto X e pronto, acabou...

Partiu combinar os patterns :). Decora um imposto com o outro e vc vai ter um imposto composto na sua cadeia :).

Então, como disse antes, consegui resolver, minha dúvida é somente se isso continua sendo um Chain of Responsability:

public class CalculadorDeDescontos {
    private List<Desconto> descontos;

    public CalculadorDeDescontos() {
        this.descontos = Arrays.asList(
                new DescontoPorCincoItens(), 
                new DescontoPorMaisDeQuinhentosReais(),
                new DescontoPorVendaCasada()
        );
    }

    public double calcula(Orcamento orcamento) {
        return descontos.stream().mapToDouble(desconto -> desconto.desconta(orcamento)).sum();
    }
}
public interface Desconto {

    public double desconta(Orcamento orcamento);
}
public class DescontoPorCincoItens implements Desconto {

    public double desconta(Orcamento orcamento) {
        if(orcamento.getItens().size() > 5)
            return orcamento.getValor() * 0.1;
        return 0;
    }
}
public class DescontoPorMaisDeQuinhentosReais implements Desconto{

    public double desconta(Orcamento orcamento) {
        if(orcamento.getValor() > 500.00)
            return orcamento.getValor() * 0.07;
        return 0;
    }

}
public class DescontoPorVendaCasada implements Desconto {

    public double desconta(Orcamento orcamento) {
        if (isVendaCasada(orcamento))
            return orcamento.getValor() * 0.05;
        return 0;
    }

    private boolean isVendaCasada(Orcamento orcamento) {
        return existe("CANETA", orcamento) && existe("LAPIS", orcamento);
    }

    private boolean existe(String nomeDoItem, Orcamento orcamento) {
        return orcamento.getItens().stream()
                .anyMatch(item -> item.getNome().equals(nomeDoItem));
    }
}
public class Item {

    public String nome;
    public double valor;

    public Item(String nome, double valor) {
        this.nome = nome;
        this.valor = valor;
    }

    public Object getNome() {
        return nome;
    }

    public double getValor() {
        return valor;
    }

}
public class Orcamento {

    private double valor;
    private final List<Item> itens;

    public Orcamento() {
        itens = new ArrayList<Item>();
    }

    public double getValor() {
        return valor;
    }

    public List<Item> getItens() {
        return Collections.unmodifiableList(itens);
    }

    public void adicionaItens(Item item) {
        itens.add(item);
        atualizaValor(item);
    }

    private void atualizaValor(Item item) {
        valor += item.getValor();
    }

}

Bom, eu tinha imaginado sua implementação de outro jeito.. desse jeito ficou mais parecido com um observer.. Você tem uma lista de observadores que reagem a um evento, nesse caso um novo orçamento.

De todo jeito, desapegando do nome do pattern, acho que ficou uma boa solução.

Acho que sim o padrão não está atrelado a implementação o que você fez foi definir a cadeia através de uma lista, você abstraiu o padrão para funcionar assim a forma que você implementou que é diferente mas para o chain o que importa é: Você sabe quem vai tratar a solicitaçao nessa lista se sim então não é chain se um ou mais objetos podem tratar essa solicitação mas não sabemos quais objetos são esses então temos o chain.

Uma das grandes dificuldades de se entender padrões é abstrair eles para contexto do problema e não para padrões de implementação.

Espero ter ajudado e bons estudos