Alguém pode me ajudar a melhorar esse código?

Alguém pode me ajudar a escreve-lo de um jeito melhor?
public class Main {

public static void main(String[] args) {
   
    int opcao = 0;
   
   
    try (Scanner scanner = new Scanner(System.in)) {
       
        do {
           
            System.out.print("Digite a opção de acordo com o que deseja fazer!"
                    + "\n" + "1 --> Cadastrar fruta"
                    + "\n" + "2 --> Consultar frutas cadastradas"
                    + "\n" + "3 --> Encerrar \n");
            opcao = scanner.nextInt();
           
            switch (opcao) {
                case 1:
                    System.out.println("Informe o nome da fruta:");
                    String nomeFruta = scanner.next().toUpperCase();
                    Fruta.novaFruta.setNome(nomeFruta);
                    System.out.println("Informe a quantidade de frutas deste tipo:");
                    int qtdFruta = Integer.parseInt(scanner.next().toUpperCase());
                    Fruta.novaFruta.setQtd(qtdFruta);
                    Fruta.cadastraFruta();
                    System.out.println("\n");
                    break;
                case 2:
                    Fruta.listarFrutas();
                    break;
                case 3:
                    System.out.println("Encerrando... \n");
                    scanner.close();
                    return;
                default:
                    System.out.println("Opção Inválida! \n");
                    break;
            }
        }
        while (opcao != 1 || opcao != 2 || opcao != 3);
       
    }
   
}

}

Peça o nome e a quantidade da Fruta pelo construtor.
Esse campo novaFruta é um design estranho, que pode ser substituído usando o construtor.
No método cadastraFruta, peça a Fruta como parâmetro.
O campo total pode ser substituído por uma variável local.
E por último daria à outra classe a responsabilidade de armazenar as frutas.

1 curtida

Vishi, pode me dar uma idéia de como fazer isso?

public Fruta(String nome, int quantidade) {
    this.nome = nome;
    this.quantidade = quantidade;
}

Aí chama o construtor assim: new Fruta("banana", 12);

public void cadastraFruta(Fruta fruta) {
    listaFrutas.add(fruta);
}

Chama o método assim:

cadastraFruta(new Fruta("maçã", 1));
//ou
Fruta abacaxi = new Fruta("abacaxi", 3);
cadastraFruta(abacaxi);

Quando você usa um bloco try desta maneira, ele já fecha automaticamente o recurso criado, então chamar scanner.close() novamente é desnecessário. Mas no caso específico do System.in, não se deve fechá-lo, então eu não usaria o bloco try-with-resources com o System.in.


Essa condição do while está errada, e só “funciona” porque na opção 3 você coloca um return, que encerra a execução do método main (experimente tirar o return para ver como ele não encerra o loop).

Na verdade deveria ser apenas while (opcao != 3), já que a única condição para encerrar o while é quando a opção é 3, então qualquer valor diferente disso deve continuar o loop.


Outro detalhe é que se está lendo dados digitados pelo teclado, é preferível usar nextLine(). Usar next() não pega a linha inteira: por exemplo, se o usuário digitar “banana nanica”, next() retorna apenas “banana”, e “nanica” será lido pelo próximo next() (que será passado para Integer.parseInt, dando erro).

E no caso do número, é completamente desnecessário usar toUpperCase() (pois se digitar um número válido, como “123”, não fará diferença nenhuma transformar para maiúsculas).


E eu também mudaria a forma como está usando a classe Fruta. Aí em cima sugeriram mudar um pouco, o que já melhorou bastante, mas eu ainda acho estranho a fruta ter a própria quantidade dela. Uma fruta tem só o nome dela, já a quantidade não faz parte da fruta, e sim do estoque (diferentes estoques podem ter quantidades diferentes da mesma fruta).

Claro que a implementação depende dos requisitos de cada sistema, mas eu entendo que a fruta é uma coisa independente da quantidade desta, então faria mais sentido ter uma outra classe que cuida do estoque. Uma implementação simples seria:

public class Fruta {
    private String nome;
    public Fruta(String nome) {
        this.nome = nome;
    }
    public String getNome() {
        return nome;
    }

    // equals e hashcode para poder usar a fruta no Map do Estoque
    @Override
    public int hashCode() {
        return Objects.hash(this.nome);
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (!(obj instanceof Fruta))
            return false;
        Fruta other = (Fruta) obj;
        return this.nome.equals(other.nome);
    }
}

public class Estoque {
    private Map<Fruta, Integer> frutas = new HashMap<>();

    public void adicionar(Fruta fruta, int quantidade) {
        // obtém a quantidade atual da fruta, ou zero se ainda não estiver no estoque
        int qtdAtual = this.frutas.getOrDefault(fruta, 0);
        // atualiza a quantidade
        this.frutas.put(fruta, quantidade + qtdAtual);
    }

    public void listar() { // mostrar as frutas e respectivas quantidades
        System.out.println("Frutas:");
        for (Entry<Fruta, Integer> entry : this.frutas.entrySet()) {
            System.out.printf(" - %s  |  %d\n", entry.getKey().getNome(), entry.getValue());
        }
    }
}

Quanto ao while, eu faria diferente. Eu prefiro um loop infinito que é interrompido por um break. Juntando com as classes Fruta e Estoque acima:

Scanner scanner = new Scanner(System.in);
Estoque estoque = new Estoque();
while (true) {
    System.out.print("Digite a opção de acordo com o que deseja fazer!\n"
        + "1 --> Cadastrar fruta\n"
        + "2 --> Consultar frutas cadastradas\n"
        + "3 --> Encerrar\n");
    int opcao = Integer.parseInt(scanner.nextLine());
    if (opcao == 3) {
        System.out.println("Encerrando...\n");
        break; // sai do while
    } else if (opcao == 1) {
        System.out.println("Informe o nome da fruta:");
        String nomeFruta = scanner.nextLine().toUpperCase();
        System.out.println("Informe a quantidade de frutas deste tipo:");
        int qtdFruta = Integer.parseInt(scanner.nextLine());
        estoque.adicionar(new Fruta(nomeFruta), qtdFruta);
        System.out.println("\n");
    } else if (opcao == 2) {
        estoque.listar();
    } else {
        System.out.println("Opção Inválida!\n");
    }
}

Claro que dá para melhorar mais. Se for digitado uma opção que não é um número (por exemplo, “xyz”), vai dar erro. Então talvez possa ter um método só para validar isso, algo do tipo:

static int lerNumero(Scanner scanner) {
    while (true) {
        try {
            return Integer.parseInt(scanner.nextLine());
        } catch (NumberFormatException e) {
            System.out.println("Você não digitou um número");
        }
    }
}

E no main basta mudar para:

Scanner scanner = new Scanner(System.in);
Estoque estoque = new Estoque();
while (true) {
    System.out.print("Digite a opção de acordo com o que deseja fazer!\n"
        + "1 --> Cadastrar fruta\n"
        + "2 --> Consultar frutas cadastradas\n"
        + "3 --> Encerrar\n");
    int opcao = lerNumero(scanner); // <------ usar o método aqui
    if (opcao == 3) {
        System.out.println("Encerrando...\n");
        break; // sai do while
    } else if (opcao == 1) {
        System.out.println("Informe o nome da fruta:");
        String nomeFruta = scanner.nextLine().toUpperCase();
        System.out.println("Informe a quantidade de frutas deste tipo:");
        int qtdFruta = lerNumero(scanner); // <------ usar o método aqui
        estoque.adicionar(new Fruta(nomeFruta), qtdFruta);
        System.out.println("\n");
    } else if (opcao == 2) {
        estoque.listar();
    } else {
        System.out.println("Opção Inválida!\n");
    }
}
2 curtidas

~Muito obrigado pela explicação