」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 專案挑戰 - 創建計數器應用程式

專案挑戰 - 創建計數器應用程式

發佈於2024-11-03
瀏覽:307

Chegamos ao segundo desafio de projeto desse módulo inicial, a última tarefa antes de passarmos para o módulo de Programação Orientada a Objetos.
Confesso que achei esse desafio meio besta e podia ser um desafio de código sem problemas, mas tudo bem.

Desafio Proposto:

O sistema deverá receber dois parâmetros via terminal que representarão dois números inteiros, com estes dois números você deverá obter a quantidade de interações (for) e realizar a impressão no console (System.out.print) dos números incrementados, exemplo:

  • Se você passar os números 12 e 30, logo teremos uma interação (for) com 18 ocorrências para imprimir os números, exemplo: "Imprimindo o número 1", "Imprimindo o número 2" e assim por diante.
  • Se o primeiro parâmetro for MAIOR que o segundo parâmetro, você deverá lançar a exceção customizada chamada de ParametrosInvalidosException com a segunda mensagem: "O segundo parâmetro deve ser maior que o primeiro"

Sugestão de passos:

  1. Crie o projeto DesafioControleFluxo
  2. Dentro do projeto, crie a classe Contador.java para realizar toda a codificação do nosso programa.
  3. Dentro do projeto, crie a classe ParametrosInvalidosException que representará a exceção de negócio no sistema.

Não é, como podem ver, a coisa mais complexa do mundo mas abre algumas possibilidades interessantes.
Podemos começar pelo final e criando a exceção customizada (quero dizer, depois de criar o projeto né):

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

Esse é o jeito mais simples de definir uma exceção customizada. Criamos uma classe (e, seguindo um padrão de nomenclatura, colocamos o sufixo Exception) que estende de Exception quando ela for uma exceção verificada (checked exception) ou de RuntimeException, se ela não for verificada (unchecked exception). Aí definimos o construtor da classe de acordo com o comportamento que queremos que nossa exceção tenha. Nesse caso ela não vai fazer muita coisa, então vamos dar uma melhorada nela.

Ai Lucas mas o que é um construtor de classe?? Você nunca disse nada sobre isso!!1!

Realmente, não foi mencionado ainda o que é um construtor. O próximo módulo, de Orientação a Objetos, deve corrigir essa lacuna de conhecimento. Segura a emoção, bicho.

No caso da nossa exceção, eu decidi que ela deve ser uma exceção verificada (ou seja, ela deve ser tratada quando o método que a lança é utilizado) para garantir que a regra de negócio Se o primeiro parâmetro for MAIOR que o segundo parâmetro, você deverá lançar a exceção customizada chamada de ParametrosInvalidosException com a segunda mensagem: "O segundo parâmetro deve ser maior que o primeiro" seja respeitada e haja um tratamento de erro a fim de não quebrar a aplicação.

Para termos mais flexibilidade na hora do tratamento, podemos definir construtores adicionais na Exception, cada um fazendo uma coisa diferente:

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

Vamos olhar com calma esse troço.
Declaramos a classe InvalidParametersException estendendo a classe Exception. Isso quer dizer, como já mencionei acima, que qualquer classe que chame o método que lance essa exceção precisa implementar um tratamento para ela.

Em seguida, declaramos a constante DEFAULT_MESSAGE, atribuindo "Um ou mais argumentos são inválidos." a ela. Como sabemos que é uma constante? Por causa da palavra reservada final e do uso do SCREAMING_SNAKE_CASE, um padrão comum para designar constantes. Esse valor vai ser exibido caso a exceção seja lançada sem argumentos, como definido no primeiro construtor: Ele chama o construtor da superclasse (no caso, a Exception) passando DEFAULT_MESSAGE. Então no caso de termos um parâmetro inválido, se uma mensagem customizada não for definida, o erro mostrado será "Um ou mais argumentos são inválidos.".

O segundo construtor permite que a exceção seja lançada com uma mensagem personalizada. Ou seja, podemos substituir as mensagens de erro padrão com alguma mensagem customizada. Por exemplo, em vez de mostrar algo como Exception in thread "main" java.lang.NullPointerException, podemos exibir Você me instruiu a acessar algo que estava nulo. Tá tudo bem, amigo?.

Os dois últimos tem um argumento diferenciado, o cause. Ele é um Throwable (um lançável) e é utilizado para relançar uma exceção. Por exemplo, vamos supor que em alguma aplicação sua exista um ponto que precise fazer uma divisão entre dois dados informados pelo usuário e o denominador (lembra dele das aulas de fração?) acaba sendo 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  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 



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!

版本聲明 本文轉載於:https://dev.to/lnabesima/desafio-de-projeto-02-criando-uma-aplicacao-contadora-39li?1如有侵犯,請聯絡[email protected]刪除
最新教學 更多>
  • 初級後端開發人員尋求無償工作以獲得經驗
    初級後端開發人員尋求無償工作以獲得經驗
    大家好, 我叫 Harith,是初級後端開發人員。我對使用 Python 和 Django 框架進行 Web 開發非常感興趣。我希望透過為開源專案做出貢獻來獲得更多實務經驗。 我提供什麼: 願意在沒有任何報酬的情況下參與項目,因為我的主要目標是提高我的技能和拓寬我的知識。 了解 Django 和...
    程式設計 發佈於2024-11-08
  • 如何在 Python 中檢查清單是否共用任何項目?
    如何在 Python 中檢查清單是否共用任何項目?
    在Python 中測試清單是否共用任何項目簡介在Python 中處理多個清單時,通常需要確定是否有元素重疊在這些列表之間。這是各種數據分析和操作任務的基本操作。 簡答在 Python 中測試列表重疊的建議方法是利用 not set(a).isdisjoint(b ) 表達。它為此任務提供了一種普遍有...
    程式設計 發佈於2024-11-08
  • Node.js 中與 WebSockets 和 Socket.IO 的即時通信
    Node.js 中與 WebSockets 和 Socket.IO 的即時通信
    现代 Web 应用程序通常需要实时通信,无论是聊天系统、实时更新、协作编辑还是通知。传统的 HTTP 通信不足以满足实时应用程序的需要,因为它依赖于请求-响应模式。这就是 WebSockets 发挥作用的地方,它允许服务器和客户端之间进行全双工通信。 在本文中,我们将探讨: WebSocket 是什...
    程式設計 發佈於2024-11-08
  • 如何解決使用 JavaScript 更改 iframe src 的問題
    如何解決使用 JavaScript 更改 iframe src 的問題
    使用 JavaScript 更改 iframe src:疑難排解點擊單選按鈕時更改 iframe 的 src 屬性時遇到問題。要糾正此問題,必須檢查代碼以確定確切的原因。一個可能的問題是括號的錯誤使用。 在您的程式碼中,該行:document.getElementById['calendar'].s...
    程式設計 發佈於2024-11-08
  • 為什麼 `window.onscroll` 在 iPhone/iPad 上不起作用?
    為什麼 `window.onscroll` 在 iPhone/iPad 上不起作用?
    在iPhone/iPad 上使用滾動事件捕獲事件嘗試在iPad 上捕獲滾動事件時,故障排除工作揭示了常見的方法例如window.onscroll 和document.onscroll 無法觸發所需的反應。 瞭解 iOS 上的事件處理裝置iPhoneOS 事件處理機制與傳統桌面瀏覽器不同。在連續的一指...
    程式設計 發佈於2024-11-08
  • 癮君子 # 何時使用效果、Angular DI 功能、請求快取等
    癮君子 # 何時使用效果、Angular DI 功能、請求快取等
    ?嘿,Angular Addict 夥伴 這是 Angular Addicts Newsletter 的第 30 期,這是一本每月精選的引起我注意的 Angular 資源合集。 (這裡是第29期、28期、27期) ?發佈公告 ?Nx 19.8 更新 ...
    程式設計 發佈於2024-11-08
  • 從開發人員到審閱者:初級開發人員審閱資料庫查詢的清單
    從開發人員到審閱者:初級開發人員審閱資料庫查詢的清單
    作為開發人員,提供高品質的程式碼至關重要,這些程式碼不僅具有功能性,而且還針對效能進行了最佳化。在開發人員領域的三年裡,我從實務開發人員轉變為審閱者角色。我在審核過程中關注的關鍵領域之一是資料庫查詢優化。 為什麼關注資料庫查詢? 資料庫查詢可以顯著影響應用程式的效能。編寫得好的查詢可以有效地獲取...
    程式設計 發佈於2024-11-08
  • Mockito 是最好的 Java 模擬框架嗎?  對其優缺點的綜合評估。
    Mockito 是最好的 Java 模擬框架嗎? 對其優缺點的綜合評估。
    最佳 Java 模擬框架:Mockito在 Java 中,製作模擬物件對於有效的單元測試至關重要。鑑於選擇過多,為此目的確定最佳框架可能會令人畏懼。本文評估了最突出的選擇之一 Mockito,重點介紹了它的優點和缺點。 Mockito 因其用戶友好的語法而脫穎而出,使其易於開發人員使用。其簡化方法針...
    程式設計 發佈於2024-11-08
  • 如何可靠地取得目前運行的Python檔案的路徑?
    如何可靠地取得目前運行的Python檔案的路徑?
    如何取得目前執行的Python檔案的路徑問題:確定目前運行的Python檔案的路徑可能很麻煩,特別是當遇到在特定場景下證明不可靠的方法時​​。其中包括從另一個腳本或在 IDLE 或 Mac OS X v10.6 等特定環境中啟動執行的實例。 解決方案:通用取得目前執行的Python 的文件路徑文件,...
    程式設計 發佈於2024-11-08
  • Stack Overflow 如何建立這些豐富的彈出訊息?
    Stack Overflow 如何建立這些豐富的彈出訊息?
    複製 Stack Overflow 的彈出訊息功能您可能已經注意到 Stack Overflow 上出現的時尚且內容豐富的彈出訊息。這些訊息為用戶提供了有價值的通知和指導,您可能想知道如何在自己的網站上實現類似的功能。 Stack Overflow 利用 HTML、CSS 和 JavaScript ...
    程式設計 發佈於2024-11-08
  • 為什麼 Python 中沒有元組理解?
    為什麼 Python 中沒有元組理解?
    理解 Python 中元組推導式的缺失在 Python 程式語言中,列表推導式和字典推導式提供了產生結構化資料的有效方法。然而,缺乏元組理解是一個異常現象。本文深入探討了這項遺漏背後的原因。 元組不變性是原因的假設並不成立。元組確實是不可變的,但這個屬性並不妨礙它們在推導式中建構。 問題的關鍵在於 ...
    程式設計 發佈於2024-11-08
  • 如何使用 VLC 模組在 Python 中播放 MP3 歌曲?
    如何使用 VLC 模組在 Python 中播放 MP3 歌曲?
    使用 Python 播放 MP3 歌曲使用正確的工具,在 Python 中播放 MP3 歌曲可以非常簡單。 錯誤的做法:嘗試使用wave模組開啟MP3文件,如下圖所示不建議:import wave w = wave.open("e:/LOCAL/Betrayer/Metalik Klini...
    程式設計 發佈於2024-11-08
  • 如何為Apache PHP應用程式配置環境變數?
    如何為Apache PHP應用程式配置環境變數?
    Apache PHP 應用程式的環境變數配置開發依賴環境變數的PHP 應用程式時,必須清楚了解如何配置環境變數使用Apache 時設定這些變數。本文旨在提供有關配置可在 PHP 中存取的環境變數的指導,確保 Web 應用程式的正確運作。 具體來說,為同一伺服器中的各個網域配置單獨的環境變數是常見的要...
    程式設計 發佈於2024-11-08
  • 如何從 Activity 存取 ViewPager 片段方法?
    如何從 Activity 存取 ViewPager 片段方法?
    從 Activity 存取 ViewPager Fragment 方法從 Activity 存取 ViewPager Fragment 方法許多行動應用程式使用片段,即代表模組化螢幕部分的獨立元件。使用視圖分頁器管理多個片段可實現流暢的導覽和頁面動畫。有時,開發人員需要在片段中執行特定操作以回應外部...
    程式設計 發佈於2024-11-08
  • 如何在 Python 中按列值對散佈圖著色?
    如何在 Python 中按列值對散佈圖著色?
    以列值為散佈圖著色在Python 中,Matplotlib 函式庫提供了多種自訂散佈圖美觀的方法。一項常見任務是根據特定列中的值指派顏色。 Seaborn 整合一個解決方案是利用基於 Matplotlib 建構的 Seaborn 函式庫。 Seaborn 提供 sns.relplot 和 sns.F...
    程式設計 發佈於2024-11-08

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3