Rumah  >  Artikel  >  Java  >  Cabaran Projek - Mencipta aplikasi kaunter

Cabaran Projek - Mencipta aplikasi kaunter

王林
王林asal
2024-09-03 10:46:32954semak imbas

Kami telah mencapai cabaran reka bentuk kedua modul awal ini, tugas terakhir sebelum beralih ke modul Pengaturcaraan Berorientasikan Objek.
Saya mengaku bahawa saya mendapati cabaran ini agak bodoh dan ia mungkin merupakan cabaran kod tanpa sebarang masalah, tetapi tidak mengapa.

Cabaran yang dicadangkan:

Sistem mesti menerima dua parameter melalui terminal yang akan mewakili dua integer, dengan kedua-dua nombor ini anda mesti mendapatkan bilangan interaksi (untuk) dan mencetak nombor yang ditambah dalam konsol (System.out.print), contohnya :

  • Jika anda melepasi nombor 12 dan 30, maka kami akan mengadakan interaksi (untuk) dengan 18 kejadian untuk mencetak nombor, contoh: “Mencetak nombor 1”, “Mencetak nombor 2” dan seterusnya.
  • Jika parameter pertama LEBIH HEBAT daripada parameter kedua, anda mesti membuang pengecualian tersuai yang dipanggil ParametrosInvalidosException dengan mesej kedua: "Parameter kedua mesti lebih besar daripada yang pertama"

Langkah yang dicadangkan:

  1. Buat projek DesafioControleFluxo
  2. Dalam projek, buat kelas Contador.java untuk menjalankan semua pengekodan program kami.
  3. Dalam projek, cipta kelas ParametrosInvalidosException yang akan mewakili pengecualian perniagaan dalam sistem.

Ia bukan, seperti yang anda lihat, perkara yang paling kompleks di dunia tetapi ia membuka beberapa kemungkinan yang menarik.
Kita boleh bermula pada penghujung dan mencipta pengecualian tersuai (maksud saya, selepas mencipta projek, betul):

//InvalidParametersException.java
public class InvalidParametersException extends Exception {  
    public InvalidParametersException() {  
        super();  
    }
}

Ini ialah cara paling mudah untuk menentukan pengecualian tersuai. Kami mencipta kelas (dan, mengikut corak penamaan, kami menambah akhiran Pengecualian) yang dilanjutkan daripada Pengecualian apabila ia adalah pengecualian yang diperiksa atau daripada RuntimeException, jika ia tidak ditandakan (pengecualian tidak ditanda). Kemudian kami mentakrifkan pembina kelas mengikut tingkah laku yang kami mahu pengecualian kami miliki. Dalam kes ini, dia tidak akan berbuat banyak, jadi mari kita perbaiki dia.

Oh Lucas, tetapi apakah itu pembina kelas?? Anda tidak pernah berkata apa-apa mengenainya!!1!

Ia benar-benar belum disebut lagi apa itu pembina. Modul seterusnya, Orientasi Objek, harus membetulkan jurang pengetahuan ini. Tahan emosi anda, haiwan.

Dalam kes pengecualian kami, saya memutuskan bahawa ia mestilah pengecualian yang diperiksa (iaitu ia mesti dikendalikan apabila kaedah yang membuangnya digunakan) untuk memastikan bahawa peraturan perniagaan Jika parameter pertama LEBIH HEBAT daripada parameter kedua, anda mesti membuang pengecualian tersuai yang dipanggil ParametrosInvalidosException dengan mesej kedua: "Parameter kedua mesti lebih besar daripada yang pertama" dihormati dan terdapat ralat pengendalian untuk tidak memecahkan aplikasi

Untuk mempunyai lebih fleksibiliti semasa mengendalikan, kami boleh menentukan pembina tambahan dalam Pengecualian, setiap satu melakukan sesuatu yang berbeza:


// InvalidParametersException.java
public class InvalidParametersException extends Exception {  
    private static final String DEFAULT_MESSAGE = "Um ou mais argumentos são inválidos.";  

    public InvalidParametersException() {  
        super(DEFAULT_MESSAGE);  
    }  
    public InvalidParametersException(String message) {  
        super(message);  
    }  
    public InvalidParametersException(String message, Throwable cause) {  
        super(message, cause);  
    }  
    public InvalidParametersException(Throwable cause) {  
        super(DEFAULT_MESSAGE, cause);  
    }}
Mari kita lihat dengan tenang perkara ini.

Kami mengisytiharkan kelas InvalidParametersException dengan melanjutkan kelas Exception. Ini bermakna, seperti yang saya nyatakan di atas, mana-mana kelas yang memanggil kaedah yang membuang pengecualian ini perlu melaksanakan rawatan untuknya.

Seterusnya, kami mengisytiharkan DEFAULT_MESSAGE malar, memperuntukkan "

Satu atau lebih argumen adalah tidak sah." kepadanya. Bagaimana kita tahu ia adalah pemalar? Kerana kata simpanan terakhir dan penggunaan SCREAMING_SNAKE_CASE, corak biasa untuk menetapkan pemalar. Nilai ini akan dipaparkan jika pengecualian dilemparkan tanpa hujah, seperti yang ditakrifkan dalam pembina pertama: Ia memanggil pembina kelas super (dalam kes ini, Pengecualian) melepasi DEFAULT_MESSAGE. Jadi sekiranya kita mempunyai parameter yang tidak sah, jika mesej tersuai tidak ditentukan, ralat yang ditunjukkan ialah "Satu atau lebih argumen tidak sah.".

Pembina kedua membenarkan pengecualian dilemparkan dengan mesej tersuai. Dalam erti kata lain, kita boleh menggantikan mesej ralat standard dengan mesej tersuai. Sebagai contoh, daripada menunjukkan sesuatu seperti Exception dalam thread "utama" java.lang.NullPointerException, kami boleh memaparkan Anda mengarahkan saya untuk mengakses sesuatu yang batal. Adakah anda sihat, kawan?.

Dua yang terakhir mempunyai hujah yang berbeza, puncanya. Ia adalah Throwable (boleh dibaling) dan digunakan untuk membaling semula pengecualian. Sebagai contoh, katakan dalam sesetengah aplikasi anda terdapat satu titik yang perlu membuat pembahagian antara dua data yang dimasukkan oleh pengguna dan penyebut (ingat dari kelas pecahan?) akhirnya menjadi 0.

Desafio de Projeto - Criando uma aplicação contadora

Lembra quando esse formato de meme era popular? Bons tempos aqueles...

Isso vai lançar uma ArithmeticException, mas o que você quer na verdade é usar sua exceção customizada novinha, que deu tanto duro para criar. Aí você pode fazer algo assim:

try {
    Scanner sc = new Scanner(System.in);
    int num1 = sc.nextInt(); //10
    int num2 = sc.nextInt(); //0
    int result = num1 / num2; //OH SHI-
} catch (ArithmeticException ex) {
    throw new InvalidParametersException("Não pode dividir por 0, bicho", ex);
}

Ou seja, o bloco try-catch captura a ArithmeticException, pega o motivo de ter sido lançada e passa para a exceção customizada, que vai disponibilizar uma mensagem mais amigável para o usuário E mostrar a causa dessa pataquada toda. A pilha de execução (stack trace) para essa situação poderia ser mais ou menos assim:

Exception in thread "main" InvalidParametersException: Não pode dividir por 0, bicho
    at Main.main(Main.java:10)  
    Caused by: java.lang.ArithmeticException: / by zero
        at Main.main(Main.java:9)

Desafio de Projeto - Criando uma aplicação contadora
Temos nossa mensagem amigável na primeira linha da pilha mas também temos o ponto exato onde as coisas deram errado, para termos uma depuração mais rápida.

Nossa, quanta coisa. E ainda nem entramos no método propriamente dito.
Aqui eu tenho que fazer uma mea-culpa: Eu reclamei a um tempo atrás de como na documentação da classe Scanner mostra que existe um método específico para capturar cada tipo primitivo e fui induzido a achar que esse era o único jeito de fazer isso. Mas conversando no Discord da Orange Juice, meu chegado Claudio me disse que tem um jeito muito mais simples:
Desafio de Projeto - Criando uma aplicação contadora
Ou seja, eu fiquei reclamando reclamando reclamando mas aparentemente dá pra fazer exatamente como no C#, como eu falei que era melhor. Claro, não dá pra aceitar tudo cegamente, então eu fui fazer um teste:

public static void main(String[] args) {  
    Scanner scanner = new Scanner(System.in);  
    System.out.print("Insira o primeiro número: ");  
    int num1 = Integer.parseInt(scanner.nextLine());  
    System.out.print("Insira o segundo número: ");  
    int num2 = Integer.parseInt(scanner.nextLine()); 

    System.out.println("O primeiro número foi: " + num1 + " e o segundo foi: " + num2);  
}

Desafio de Projeto - Criando uma aplicação contadora

Não é que deu certo, gente?
Eu to me sentindo bem mal agora, não fiz a coisa mais simples antes de começar a reclamar... Que burro, dá zero pra mim.
Desafio de Projeto - Criando uma aplicação contadora

Muito bem. Me desculpe, menino Java.
Isto posto, podemos começar a desenvolver nosso método de contagem. A classe Counter vai ter dois métodos: o counter, que vai conter a lógica da contagem e o main, que vai chamar o contador e tratar as exceções que surgirem. Vamos começar com o counter:

public class Counter {  
    public static void main(String[] args) {}  //por enquanto esse método segue vazio

    public static void count(int num1, int num2) throws InvalidParametersException {  
        if (num1 < 0 || num2 < 0) throw new InvalidParametersException();  
        if (num1 > num2) throw new InvalidParametersException("O primeiro argumento deve ser maior que o segundo");  

        int counter = num2 - num1;  
        System.out.printf("Vou imprimir todos os números de 1 até %d.%n", counter);  

        for (int i = 1; i <= counter; i++) {  
            System.out.printf("Imprimindo o número %d%n", i);  
        }    
    }
}

Declaramos o método counter, que é público e não tem retorno (podemos ter certeza por conta do void ali). Além disso, esse método é estático, então não precisamos instanciar a classe para podermos usá-lo. Ele recebe dois argumentos do tipo int, num1 e num2 -- criatividade é a alma do negócio. Por fim, podemos ver que esse método lança a nossa maravilhosa exceção InvalidParametersException, o que vai obrigar o método main a realizar algum tipo de tratamento em cima dela.

Para garantir que as regras de negócio vão ser respeitadas, o método faz duas verificações:

  1. Checa se qualquer um dos números é negativo e, em caso positivo lança uma exceção informando ao usuário que um ou mais argumentos são inválidos;
    • Essa checagem, apesar de não ter sido pedida, é para garantir que não haveria resultados esquisitos na subtração feita. Eu não queria, por exemplo, ter que lidar com resultados negativos.
  2. Checa se o primeiro número é maior do que o segundo. Se sim, lança uma exceção orientando o usuário que o segundo número (o numerador da divisão) deve ser maior.

Depois disso, é realizada a subtração que vai montar o loop. Feita essa conta, é impressa no console uma mensagem informando que a aplicação irá mostrar todos os números de 1 até o resultado.

E aqui vem mais uma particularidade do Java: a falta de interpolação de strings. Eu me acostumei a escrever

const variable = variable;
let sentence = `I'm a sentence that uses a ${variable}!`;

ou

string name = "John Doe";
string sentence = $"My name is {name}.";

que é até esquisito imaginar uma linguagem moderna que não utiliza isso.

Para ficar melhor (e evitar usar a concatenação com aquela sintaxe horrorosa), descobri que poderia formatar a mensagem com o método String.format(), da mesma forma que a formatação de string do C. E assim como no C, existe a possibilidade de já formatar a string usando o método print. Em vez de usar o .println() para imprimir uma string e já pular uma linha (o ln significa que caractere para newline será adicionado no final), o método .printf() formata a mensagem de acordo com o placeholder informado.
Existem muitos placeholders que podem ser utilizados, mas os mais comuns são %s para string, %d para int, %f para números de ponto flutuante (como double e float) e %f para data/hora. Porém esse método não cria uma quebra de linha então caso seja necessário, é preciso adicionar o caractere %n para uma nova linha.

Aí, por último, fazemos nosso laço for, sem grandes mistérios. Inicializamos o contador do loop em 1 e o instruímos a repetir a tarefa até o chegar no resultado final, incrementando o valor do contador de um em um. A cada volta imprimimos o valor do contador atual, cumprindo, desse modo, o requisito do desafio.

Beleza, criamos o método que vai realizar a ação que precisamos. Agora, o que falta é chamar a função e executá-la em algum lugar, né? Vamos fazer isso no método main da nossa classe Counter:

public class Counter {  
    public static void main(String[] args) {  
        try {  
            Scanner scanner = new Scanner(System.in);  
            System.out.println("Insira dois números e a aplicação imprimirá a diferença entre eles, linha a linha.");  
            System.out.print("Insira o primeiro número: ");  
            int num1 = Integer.parseInt(scanner.nextLine());  
            System.out.print("Insira o segundo número: ");  
            int num2 = Integer.parseInt(scanner.nextLine());  

            count(num1, num2);  

        } catch (InvalidParametersException e) {  
            System.out.println(e.getMessage());  
        }    
    }  
    public static void count(int num1, int num2) throws InvalidParametersException { /* lógica descrita acima */ }

Aqui não tem nada muito excepcional: Instanciamos um novo scanner e, seguindo a dica valiosa do Cláudio, pedimos ao usuário que insira dois números. Esses números são capturados como string e imediatamente convertidos em int. Com esses dados, chamamos a função count passando os dois números como parâmetros e, caso alguma exceção seja lançada, uma mensagem de erro será exibida no console.

Show, mas será que funciona?

Desafio de Projeto - Criando uma aplicação contadora
Desafio de Projeto - Criando uma aplicação contadora
Desafio de Projeto - Criando uma aplicação contadora

Aparentemente sim. ✌️
Mas o que acontece se, por exemplo, algum usuário espírito de porco curioso inserisse um número muito grande em um dos campos? Será que a aplicação daria conta do recado?
Desafio de Projeto - Criando uma aplicação contadora
Bom, não né. O int, destino da conversão da string nos inputs, aloca 32 bits de memória para cada variável. Isso quer dizer, na prática, que o valor máximo que ele pode armazenar é 2147483647 (e o menor, -2147483648). Quando o número alvo extrapola esse limite, essa exceção NumberFormatException é lançada.
Para solucionar isso, poderíamos mudar o tipo de destino de int para long, mas o problema da limitação ainda se mantém. Claro que é bem mais difícil que alguém indique um número grande como o long (cujo valor máximo é 9223372036854775807), mas é sempre bom não dar chance pro azar. Por isso, a melhor coisa é adicionar algum tipo de limitação e informar ao usuário que ele tá maluco precisa informar um número dentro do intervalo esperado.

Além disso, a aplicação encerrar quando encontra um erro é meio chato. O ideal seria que ela voltasse a iniciar caso encontrasse um erro, até que os inputs fossem inseridos de maneira correta.

Podemos resolver adicionando um novo catch no nosso try e envolvendo a aplicação toda em um laço while:

public class Counter {  
    public static void main(String[] args) {  
        while (true) {  
            try {  
                Scanner scanner = new Scanner(System.in);  
                System.out.println("Insira dois números e a aplicação imprimirá a diferença entre eles, linha a linha.");  
                System.out.print("Insira o primeiro número: ");  
                int num1 = Integer.parseInt(scanner.nextLine());  
                System.out.print("Insira o segundo número: ");  
                int num2 = Integer.parseInt(scanner.nextLine());  

                count(num1, num2);  
                break;  
            } catch (InvalidParametersException e) {  
                System.out.println(e.getMessage());  
            } catch (NumberFormatException e) {  
                System.out.println("Um dos números informados estão acima da capacidade de processamento desta aplicação. Por favor, tente novamente com um número menor.");  
            }        
        }    
    }  
    public static void count(int num1, int num2) throws InvalidParametersException { /* lógica descrita acima */ }

A primeira coisa que fizemos foi, então, envolver todo o try-catch em um laço while, e definimos a condição como true. Ou seja, enquanto true for... verdadeiro, o laço se repetirá.
Fizemos um famigerado loop infinito, a perdição de todo processador.

Em vez de colocarmos a condição para parada do while na sua definição, apenas colocamos um break ao final da chamada ao método count(); desse modo, se não houver alguma exceção lançada, o loop será interrompido.

Ao final da chamada, definimos mais um bloco catch, capturando a exceção NumberFormatException e passando uma mensagem de erro mais fácil de ser compreendida. Bora testar pra ver se está tudo certo?
Desafio de Projeto - Criando uma aplicação contadora
Bom demais.

Agora, a única coisa que falta é chamar o método Counter.main() na classe Main. Pode ser redundante, mas eu prefiro deixar bem separadinhas e explicadas as coisas.

public class Main {  
    public static void main(String[] args) {  
        Counter.main(args);  
    }
}

Desafio de Projeto - Criando uma aplicação contadora

É isso aí, pessoal. Obrigado pela paciência e por ter lido esse post gigantesco.
O repositório desse projetinho pode ser encontrado aqui. Até a próxima!

Atas ialah kandungan terperinci Cabaran Projek - Mencipta aplikasi kaunter. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn