5
respostas

Considere a responsabilidade getNome() na interface Desconto. Como implementa-la?

Como fazer para exibir o nome do desconto? Imagine que tenho na interface Desconto a responsabilidade de retornar a descrição do mesmo. Nesse caso, cada desconto deveria implementa-lo, nesse caso, com um set ou passando o nome no construtor. Já que eu vou encadeando um desconto no outro, como fazer para exibir o nome do desconto?

5 respostas

Oi William! Tudo bem?

O problema que você possui é bem comum em sistemas, principalmente se os modelos de precificação vão se tornando complexos.

A melhor maneira de desenhar um sistema que precisa aplicar descontos de forma encadeada é criar um modelo de desconto e um modelo de regras que dizem se esse desconto se aplica ou não. Cada regra está associada a uma implementação que armazena informações relevantes, tais quais:

1) Código do desconto. Ex. #OFMAL2019 2) Nome amigável do desconto. Ex. Oferta Maluca 2019 3) Tipo de desconto. Ex. Percentual ou Valor 4) Target do desconto: Produto, Pedido, Frete, ... 5) Desconto: Ex. 100 ou 0.10 (10%) 6) Stackable: Esse produto permite que se apliquem outros descontos. 7) Prioridade: Define a regra com a qual uma promoção será aplicada. 8) Condição: Define qual é a classe de implementação que determina se a regra se aplica ou não.

Imagine um carrinho de compras, tudo bem? O usuário adiciona um produto a esse carrinho O sistema varre seu sistema de regras de preço e verifica as condições do carrinho e se aplicam ou não. Ao final, ele consegue determinar descontos e quais descontos ele aplicou e os respectivos nomes.

Para isso recomendo fortemente que estude design patterns, mais especificamente Strategy, para cada tipo de promoção que você necessitar.

Outro ponto importante é que descontos em geral se aplicam de formas bastantes distintas: 1) Dar um desconto em um produto 2) Dar um desconto no total de um carrinho se atingir um limite de compras 3) Dar desconto no frete baseado em um produto 4) Oferecer produtos grátis se comprar um ou mais produtos. 5) Descontos do tipo cash back. 6) Descontos por tipo de pagamento.

Espero que tenha ajudado,

Abraço e sucesso,

Rodrigo

Olá Rodrigo. Entendo a complexidade que podemos ter sobre essa área. Entretanto, minha questão é menor e bem mais específica. Estou estudando o padrão chain of responsability e ao implement-lo, surgiu uma dúvida.

Considere:

public class CalculadoraDeDesconto {

    public Double calcular(Pedido p) {
        Desconto d1 = new DescontoPorMaisDeMil("Mais de mil");
        Desconto d2 = new DescontoPorMaisDeCincoItens("Mais de 5 itens");
        Desconto d3 = new DescontoVendaCasada("Venda casada");
        Desconto d4 = new DescontoSemDesconto("Sem desconto");
        d2.definirProximoDesconto(d1);
        d1.definirProximoDesconto(d3);
        d3.definirProximoDesconto(d4);
        return d1.getValor(p);
    }

}
public interface Desconto {

    Double getValor(Pedido p);

    void definirProximoDesconto(Desconto d);//chain of responsability

    String getDescricao();// OBJETIVO

}
public class DescontoPorMaisDeMil implements Desconto {

    private Desconto desconto;
    private String descricao;//OBJETIVO

    public DescontoPorMaisDeMil(String descricao) {
        this.descricao = descricao;
    }

    @Override
    public Double getValor(Pedido p) {
        if (p.getValor() >= 1000) {
            return p.getValor() - (p.getValor() * .2);
        }
        return this.desconto.getValor(p);
    }

    @Override
    public void definirProximoDesconto(Desconto d) {
        this.desconto = d;
    }

    @Override
    public String getDescricao() {
        return this.descricao;
    }

}
public class Teste {

    public static void main(String[] args) {
        Pedido pedido = new Pedido();
        pedido.adicionarItem(new Item("Caneta", 600.0));
        pedido.adicionarItem(new Item("Caneta", 100.0));

        pedido.adicionarItem(new Item("Caneta", 100.0));
        pedido.adicionarItem(new Item("Lapis", 200.0));
        System.out.println("Total: " + pedido.getValor());

        CalculadoraDeDesconto calculadoraDeDesconto = new CalculadoraDeDesconto();

        Double total = calculadoraDeDesconto.calcular(pedido);
        System.out.println("Total com desconto:" + total);

    }

}

Acrescentei O OBJETIVO de imprimir a descrição do desconto. Apenas quero imprimir o valor com desconto e a descrição do desconto aplicado através da cadeia.

Obrigado.

Oi William,

Penso que é isso que você precisa.

Att,

Rodrigo

public class DescontoPorMaisDeMil implements Desconto {

    private Desconto desconto;
    private String descricao;//OBJETIVO

    public DescontoPorMaisDeMil(String descricao) {
        this.descricao = descricao;
    }

    @Override
    public Double getValor(Pedido p) {
        if (applies(p)) {
            System.out.println("Desconto " + descricao + " FOI aplicado ao pedido.");
            return calculateDiscount(p);
        } else {
            System.out.println("Desconto " + descricao + " NÃO FOI aplicado ao pedido.");
            return this.desconto.getValor(p);
        }
    }

    public boolean applies(Pedido p){
        return p.getValor() >= 1000;
    }

    public Double calculateDiscount(Pedido p){
     return p.getValor() - (p.getValor() * .2)
    }

    @Override
    public void definirProximoDesconto(Desconto d) {
        this.desconto = d;
    }

    @Override
    public String getDescricao() {
        return this.descricao;
    }

}

Olá.

Certo. Posso identificar a descrição através do método getValor(Pedido p). Entretanto, pensei em obter a descrição sem estar atrelado a esse método. Um método próprio, invocado quando necessário. Creio que para obter o objeto desconto em tempo de execução teríamos que ter outra arquitetura. Seria necessário saber qual objeto foi instanciado através da cadeia, essa é a questão.

De qualquer forma, obrigado pela prestatividade.

Existem muitas formas de mostrar qual o desconto aplicado uma bem simples é adicionando um System.out.println("") no método que aplica o desconto nas classes concretas.

public class DescontoPorValorMaior implements Desconto {

    private Desconto proximo;

    public double desconta(Orcamento orcamento){
        if (orcamento.getValor() > 500) {
            System.out.println("Desconto por maior valor");
            return orcamento.getValor() * 0.07;

        }else{
            return proximo.desconta(orcamento);
        }
    }

Se quiser algo menos hard code, você pode usar o nome da classe

public class DescontoPorCincoItens implements Desconto {

    private Desconto proximo;

    public double desconta(Orcamento orcamento) {
        if (orcamento.getItens().size() > 5) {
           System.out.println(getClass());
            return orcamento.getValor() * 0.1;
        } else {
            return proximo.desconta(orcamento);
        }
    }

E assim por diante nos outros métodos da cadeia. Espero ter ajudado e bons estudos.