>  기사  >  Java  >  프로젝트 챌린지 - 카운터 애플리케이션 만들기

프로젝트 챌린지 - 카운터 애플리케이션 만들기

王林
王林원래의
2024-09-03 10:46:32950검색

객체 지향 프로그래밍 모듈로 넘어가기 전 마지막 작업인 이 초기 모듈의 두 번째 설계 과제에 도달했습니다.
솔직히 이 챌린지는 좀 어리석었고 아무 문제도 없는 코드 챌린지였을 수도 있었지만 괜찮습니다.

제안된 챌린지:

시스템은 터미널을 통해 두 개의 정수를 나타내는 두 개의 매개변수를 수신해야 하며, 이 두 숫자를 사용하여 상호 작용 수를 가져와서 콘솔(System.out.print)에 증분된 숫자를 인쇄해야 합니다. 예를 들어 :

  • 숫자 12와 30을 전달하면 숫자를 인쇄하기 위해 18번 발생하는 상호작용이 발생합니다(예: '숫자 1 인쇄', '숫자 2 인쇄' 등).
  • 첫 번째 매개변수가 두 번째 매개변수보다 큰 경우 '두 번째 매개변수는 첫 번째 매개변수보다 커야 합니다.'라는 두 번째 메시지와 함께 ParametrosInvalidosException이라는 맞춤 예외를 발생시켜야 합니다.

권장 단계:

  1. DesafioControleFluxo 프로젝트 만들기
  2. 프로젝트 내에서 Contador.java 클래스를 생성하여 프로그램의 모든 코딩을 수행합니다.
  3. 프로젝트 내에서 시스템의 비즈니스 예외를 나타내는 ParametrosInvalidosException 클래스를 만듭니다.

보시다시피 세상에서 가장 복잡한 것은 아니지만 몇 가지 흥미로운 가능성을 열어줍니다.
끝에서 시작하여 사용자 정의 예외를 생성할 수 있습니다(즉, 프로젝트를 생성한 후입니다).

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

이것은 사용자 정의 예외를 정의하는 가장 간단한 방법입니다. 확인된 예외인 경우 Exception에서 확장되고 확인되지 않은 경우(확인되지 ​​않은 예외) RuntimeException에서 확장되는 클래스를 만듭니다(그리고 명명 패턴에 따라 접미사 Exception을 추가합니다). 그런 다음 예외에 적용하려는 동작에 따라 클래스 생성자를 정의합니다. 이런 경우에는 별로 효과가 없을 것이므로 개선해 보도록 하겠습니다.

오 루카스님, 그런데 클래스 생성자가 뭐죠?? 너는 그것에 대해 아무 말도 하지 않았어!!1!

생성자가 무엇인지는 아직까지 언급되지 않았습니다. 다음 모듈인 객체 지향에서는 이러한 지식 격차를 바로잡아야 합니다. 감정을 참아라, 동물아.

우리 예외의 경우 비즈니스 규칙을 보장하기 위해 확인된 예외(즉, 예외를 던지는 메소드가 사용될 때 처리되어야 함)여야 한다고 결정했습니다. 첫 번째 매개변수가 다음보다 큰 경우 두 번째 매개변수인 경우 두 번째 메시지인 '두 번째 매개변수는 첫 번째 매개변수보다 커야 합니다'와 함께 ParametrosInvalidosException이라는 맞춤 예외를 발생시켜야 하며 애플리케이션이 중단되지 않도록 오류 처리가 필요합니다

.

처리할 때 더 많은 유연성을 갖기 위해 예외에서 각각 다른 작업을 수행하는 추가 생성자를 정의할 수 있습니다.

// 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);  
    }}

이건 차분하게 살펴보자.
Exception 클래스를 확장하여 InvalidParametersException 클래스를 선언합니다. 이는 위에서 언급한 것처럼 이 예외를 발생시키는 메서드를 호출하는 모든 클래스가 이에 대한 처리를 구현해야 함을 의미합니다.

다음으로 "하나 이상의 인수가 유효하지 않습니다."를 할당하여 DEFAULT_MESSAGE 상수를 선언합니다. 그것이 상수라는 것을 어떻게 알 수 있나요? 최종 예약어와 상수 지정을 위한 일반적인 패턴인 SCREAMING_SNAKE_CASE 사용 때문입니다. 이 값은 첫 번째 생성자에 정의된 대로 인수 없이 예외가 발생하는 경우 표시됩니다. DEFAULT_MESSAGE를 전달하는 슈퍼클래스 생성자(이 경우 예외)를 호출합니다. 따라서 잘못된 매개변수가 있는 경우 사용자 정의 메시지가 정의되지 않은 경우 "하나 이상의 인수가 잘못되었습니다."라는 오류가 표시됩니다.

두 번째 생성자는 사용자 정의 메시지와 함께 예외가 발생하도록 허용합니다. 즉, 표준 오류 메시지를 사용자 정의된 메시지로 대체할 수 있습니다. 예를 들어, "main" java.lang.NullPointerException 스레드에서 예외와 같은 내용을 표시하는 대신, Null인 항목에 액세스하라고 지시하셨습니다. 괜찮아 친구?.

마지막 두 사람은 서로 다른 주장, 즉 원인을 가지고 있습니다. 이는 Throwable(throwable)이며 예외를 다시 발생시키는 데 사용됩니다. 예를 들어, 일부 애플리케이션에서 사용자가 입력한 두 데이터를 나누어야 하는 지점이 있고 분모(분수 클래스에서 기억하시나요?)가 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!

위 내용은 프로젝트 챌린지 - 카운터 애플리케이션 만들기의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.