One possible solution to follow. It does not have ideal architecture and performance, but it illustrates several elements of Java 8, within the constraints of the statement.
Product Class:
package br.teste.compras;
public class Produto {
private Long codigo;
private String descricao;
public Produto(Long codigo, String descricao) {
setCodigo(codigo);
setDescricao(descricao);
}
// getters and setters omitidos
}
Class Item:
package br.teste.compras;
import java.math.BigDecimal;
public class Item {
private Produto produto;
private BigDecimal valorUnitario;
private int quantidade;
public Item(Produto produto, BigDecimal valorUnitario, int quantidade) {
setProduto(produto);
setValorUnitario(valorUnitario);
setQuantidade(quantidade);
}
public BigDecimal getValorTotal() {
Multiplicador<Integer> multiplicador = (val1, val2) -> val2.multiply(BigDecimal.valueOf(val1));
return multiplicador.multiplicar(getQuantidade(), getValorUnitario());
}
@FunctionalInterface
interface Multiplicador<F> {
BigDecimal multiplicar(F val1, BigDecimal val2);
}
//getters and setters omitidos
}
Class Shopping Cart:
package br.teste.compras;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
public class CarrinhoCompras {
private List<Item> items = new ArrayList<Item>();
//São admitidas repetições de um mesmo produto.
//Para uma melhor arquitetura, o valorUnitario deveria ser atributo de Produto.
public void adicionarItem(Produto produto, BigDecimal valorUnitario, int quantidade) {
items.add(new Item(produto, valorUnitario, quantidade));
}
public boolean removerItem(Produto produto) {
return items.remove(produto); //retorna true se removido
}
public boolean removerItem(int posicaoItem) {
//Não é a implementação mais eficiente, mas ilustra o uso do Java 8.
return items.removeIf(item -> posicaoItem == items.indexOf(item));
}
public BigDecimal getValorTotal() {
return items.stream().map(Item::getValorTotal).reduce(BigDecimal.ZERO, BigDecimal::add);
}
//Único método adicionado à classe, conforme permitido pelo enunciado.
public List<Item> getItems() {
return items;
}
}
Class Shopping CartFactory:
package br.teste.compras;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;
public class CarrinhoComprasFactory {
//Armazena os clientes e seus carrinhos.
private final Map<String, CarrinhoCompras> map = new HashMap<>();
public CarrinhoCompras criar(String identificacaoCliente) {
map.put(identificacaoCliente, new CarrinhoCompras());
return map.get(identificacaoCliente);
}
//Não está clara a função deste método, mas aqui retorna o valor médio dos valores totais nos carrinhos dos clientes.
public BigDecimal getValorTicketMedio() {
return map.values().stream()
.map(carrinho -> carrinho.getValorTotal())
.reduce(BigDecimal.ZERO, BigDecimal::add)
.divide(BigDecimal.valueOf(map.size()), RoundingMode.FLOOR);
}
//Retorna true se existe esse cliente.
public boolean invalidar(String identificacaoCliente) {
return map.remove(identificacaoCliente) != null;
}
}
A test class, also with some elements of Java 8:
package br.teste.compras;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
import java.util.stream.LongStream;
public class Build {
public static void main(String[] args) {
CarrinhoComprasFactory factory = new CarrinhoComprasFactory();
Map<Long, CarrinhoCompras> map = new HashMap<>();
//Inclui 3 clientes
LongStream.range(1, 4).forEach(idx -> map.put(idx, factory.criar("Cliente " + idx)));
//adiciona 3 produtos no carrinho de cada cliente.
map.forEach((clienteID, carrinho) -> {
System.out.println("*** Cliente " + clienteID + " ***");
LongStream.range(1, 4).forEach(idx -> {
Long random = ThreadLocalRandom.current().nextLong(1, 100);
carrinho.adicionarItem(
new Produto(random * 100, "Produto " + clienteID * 1000 + idx),
BigDecimal.valueOf(random), (int)(random % 11 + 1));
});
//imprime os items gerados
System.out.println(carrinho.getItems().stream()
.map(item -> item.getProduto().getDescricao() + "(" + item.getProduto().getCodigo() + "): " +
item.getQuantidade() + " x $" + item.getValorUnitario() + " = $" + item.getValorTotal())
.collect(Collectors.joining(", ")));
});
System.out.println("Media clientes 1, 2 e 3: $" + factory.getValorTicketMedio());
factory.invalidar("Cliente 3");
map.remove(Long.valueOf(3)); // :-(
map.get(Long.valueOf(2)).removerItem(1);
map.get(Long.valueOf(1)).removerItem(map.get(Long.valueOf(1)).getItems().get(0).getProduto());
System.out.println("Media clientes 1 e 2: $" + factory.getValorTicketMedio());
}
}
As the construction of item values is random, below is an example of output printed by the above class (Build):
*** Cliente 1 ***
Produto 10001(4400): 1 x $44 = $44, Produto 10002(2700): 6 x $27 = $162, Produto 10003(6900): 4 x $69 = $276
*** Cliente 2 ***
Produto 20001(3500): 3 x $35 = $105, Produto 20002(5200): 9 x $52 = $468, Produto 20003(9500): 8 x $95 = $760
*** Cliente 3 ***
Produto 30001(6700): 2 x $67 = $134, Produto 30002(2800): 7 x $28 = $196, Produto 30003(5000): 7 x $50 = $350
Media clientes 1, 2 e 3: $831
Media clientes 1 e 2: $673