」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 練習 - 使用終端模擬銀行帳戶

練習 - 使用終端模擬銀行帳戶

發佈於2024-08-26
瀏覽:368

Para sair um pouco da teoria, o próximo passo no bootcamp é um exercício (que eles chamam de desafio de projeto). Achei interessante colocar uma parte prática para sedimentar o conteúdo abordado até aqui.

O que foi pedido foi para criar um projeto que simule uma única conta em um banco, contendo número da agência, número da conta, nome do cliente e saldo. Esses dados devem vir do terminal e ao final, deve ser exibida uma mensagem informando que a conta foi criada com sucesso e mostrando quais foram os dados inseridos. A descrição original pode ser vista aqui.

Bom, parece bem simples. Mas uma coisa que eu gosto muito de fazer em qualquer tarefa que eu recebo é quebrar em passos bem curtinhos, pra que eu tenha um caminho claro de onde seguir. Eu faço isso também porque enquanto estou desenhando o fluxo na minha cabeça, consigo entender se ele faz sentido com o que foi pedido e posso rapidamente mudar o curso de ação se achar que não está batendo.

Exercício - Simulando Uma Conta Bancária Através Do Terminal

Sendo assim, os passos a seguir são os seguintes:

  1. Criar uma classe para "abrigar" os dados recebidos no terminal e o método criador de conta;
  2. Pedir ao usuário os dados, ler as informações inseridas no terminal e armazená-las em variáveis;
  3. Exibir ao usuário a mensagem de confirmação, organizando as variáveis em uma string legível.

Não parece algo exatamente complexo. Começando pelo passo 1, temos o seguinte:

TerminalAccount.java

public class TerminalAccount { // eu sei, sou muito criativo
    private int branch;
    private String account;
    private String clientName;
    private double balance;

    public void createAccount(){
        // aqui a mágica acontece
    }
}

Apesar de modificadores de acesso não terem sido abordados com muita profundidade até agora no curso eu optei por deixar todos os campos privados, imaginando que em um sistema bancário, os dados de agência, conta, titular e saldo devem ser bem protegidos e só a própria classe deve ter acesso a eles.

Além disso, o método createAccount não deve retornar nada, apenas exibir uma mensagem no console, por isso seu retorno é void.
É nesse método que começa algumas das coisas que me irritam no Java. Em vez de ter um método genérico que captura qualquer tipo de informação que o usuário inserir, a classe Scanner, que é usada para isso, tem métodos específicos para cada tipo primitivo. Ou seja, tem um Scanner.nextLine() para Strings, Scanner.nextInt() para números, Scanner.nextBoolean para booleanos...

Exercício - Simulando Uma Conta Bancária Através Do Terminal

Em comparação, no C# temos um método de entrada padrão que sempre retorna uma String, que ter seu tipo alterado em seguida:

String input = Console.ReadLine();
int.TryParse(input, out number)
Console.WriteLine(number) //completamente válido

Tem que escrever um pouco mais? Tem. Mas pelo menos eu não preciso lembrar da sintaxe de todos os métodos, basta apenas um e converter para o tipo que eu quero em seguida. Muito mais intuitivo.
Mas sigamos, que ainda tem muito chão pela frente para resolver esse problema. O próximo passo, então, é pedir para o usuário inserir os dados do cliente para o registro.

TerminalAccount.java

public class TerminalAccount {  
    private int branch;  
    private String account;  
    private String clientName;  
    private double balance;  

    public void createAccount() {  
        Scanner sc = new Scanner(System.in);  
        System.out.print("Por favor, insira o número da agência: ");  
        this.branch = sc.nextInt();  

        System.out.print("Por favor, insira o número da conta: ");  
        this.account = sc.nextLine();  

        System.out.print("Por favor, insira o nome do cliente: ");  
        this.clientName = sc.nextLine();  

        System.out.print("Por favor, insira o saldo inicial: ");  
        this.balance = sc.nextDouble();  

        System.out.println("Olá "   this.clientName   ", obrigado por criar uma conta em nosso banco. sua agência é "   this.branch   ", conta "   this.account   " e seu saldo "   this.balance   " já está disponível para saque.");  
    }  
}

Bom, aparentemente está tudo certo. Vamos instanciar essa classe no nosso método main e ver o que dá.

Main.java

public class Main {  
    public static void main(String[] args) {  
        TerminalAccount account = new TerminalAccount();  
        account.createAccount();  
    }  
}

Exercício - Simulando Uma Conta Bancária Através Do Terminal

Oxe? Porque o input do número da conta foi ignorado e o algoritmo já pediu o nome do cliente?
Lendo a documentação da classe Scanner, é explicado que ela quebra o input em tokens usando algum tipo de caractere como delimitador, para saber onde parar. No caso dos métodos .next() e .hasNext() (e suas variantes, como o .nextInt() e o .hasNextInt()), o delimitador é um whitespace global (\s ) como espaços, tabulações e quebras de linha.

O que o método faz é pegar o token precedido pelo delimitador, converter para o tipo especificado e retornar esse valor. O resto continua no buffer do input.
Beleza, até aí ok. O problema é que o método .nextLine(), usado para capturar Strings consome o caractere de quebra de linha (\n), em vez de descartar. Aí, quando usado na sequência de outro que faz o descarte, ele lê o que ficou pra trás e imediatamente encerra sua atuação, passando para a próxima linha do código.

Parece confuso, e é mesmo. Fiz um diagrama para ajudar a compreender a loucura que é esse fluxo:

Exercício - Simulando Uma Conta Bancária Através Do Terminal

Tá, e como consertamos essa lambança? Simples: adicionando um novo Scanner.nextLine() logo depois do .nextInt() para "limpar" o que sobrou. É bonito? Não, mas resolve.
Dá pra mudar o delimitador para aceitar a quebra de linha (\n ) em vez de um whitespace comum (\s ), mas isso poderia quebrar a forma com que as informações são quebradas em tokens, então é melhor deixar pra lá.

TerminalAccount.java

public class TerminalAccount {  
    private int branch;  
    private String account;  
    private String clientName;  
    private double balance;  

    public void createAccount() {  
        Scanner sc = new Scanner(System.in);  
        System.out.print("Por favor, insira o número da agência: ");  
        this.branch = sc.nextInt();  
        sc.nextLine();

        //...
    } 
}

Mais feio que bater na mãe.

Exercício - Simulando Uma Conta Bancária Através Do Terminal

Beleza, funcionou. Poderíamos dizer que o exercício está completo, mas vamos por um segundo imaginar que o usuário, sem querer, digitou uma letra na hora de colocar o número da agência:

Exercício - Simulando Uma Conta Bancária Através Do Terminal

Eita lasqueira.

Isso acontece porque, como já sabemos, a tipagem do Java é estática e o Scanner está esperando um número mas quando colocamos uma letra junto com um número, essa cadeia se torna uma String. O input que estava esperando um int recebeu uma String e ficou confuso tal qual uma criança que recebe meias de presente de natal.
Exercício - Simulando Uma Conta Bancária Através Do Terminal

Para remediar essa situação, podemos criar um loop simples, que informe para o usuário que a informação que ele inseriu está incorreta de acordo com as especificações do sistema e pedir que ele insira os dados novamente (de maneira correta, dessa vez). Como não sabemos quantas tentativas o usuário vai levar para inserir os dados corretamente, um while parece adequado.

public class TerminalAccount {  
    private int branch;  
    private String account;  
    private String clientName;  
    private double balance;  

    public void createAccount() {  
        Scanner sc = new Scanner(System.in);  

        boolean isBranchNumberInputCorrect = false;  
        do {  
            try {  
                System.out.print("Por favor, insira o número da agência: ");  
                this.branch = sc.nextInt();  
                sc.nextLine();  
                isBranchNumberInputCorrect = true;  
            } catch (InputMismatchException e) {  
                System.out.println("Por favor, insira apenas números inteiros para o número da agência."); 
                sc.nextLine(); 
            }  
        } while (!isBranchNumberInputCorrect);

        //...
    }
}

Aqui criamos uma variável de controle chamada IsBranchNumberInputCorrect (porque, novamente, sou muito criativo quando se trata de nomes), inicializada em false. Em seguida, começamos o bloco do, uma vez que queremos que o código faça uma ação antes de verificar se o dado inserido é valido ou não e jogamos nosso input lá pra dentro.
Caso dê tudo certo, o dado inserido será armazenado no campo branch, qualquer caractere sobrando será consumido pelo Scanner.nextLine() e a nossa variável de controle será atualizada para true. Aí a condição do while vai checar se isBranchNumberInputCorrect é false. Se for, reinicia o loop no caso de sucesso, o laço é encerrado.

Agora, caso o usuário insira algo não esperado (como uma String), o método Scanner.nextInt() vai emitir um evento de erro InputMismatchException, que será capturado pelo nosso bloco catch. Uma vez lá dentro, o código vai exibir uma mensagem de erro alertando que o tipo de dado está errado e consumido qualquer caractere que tenha ficado pra trás.

Exercício - Simulando Uma Conta Bancária Através Do Terminal

A gente pode fazer a mesma coisa com o input de saldo, para garantir que o valor inserido sempre será numérico e não permitir que a aplicação quebre caso seja inserido algo como 12,56f:

public class TerminalAccount {  
    private int branch;  
    private String account;  
    private String clientName;  
    private double balance;  

    public void createAccount() {  
        Scanner sc = new Scanner(System.in); 

        //...

        boolean isBalanceInputCorrect = false;  
        do {  
            try {  
                System.out.print("Por favor, insira o saldo inicial: ");  
                this.balance = sc.nextDouble();  
                sc.nextLine();  
                isBalanceInputCorrect = true;  
            } catch (InputMismatchException e) {  
                System.out.println("Por favor, insira apenas valores decimais.");  
                sc.nextLine();  
            }  
        } while (!isBalanceInputCorrect);  

        //...
    }  
}

Exercício - Simulando Uma Conta Bancária Através Do Terminal

Poderíamos parar por aqui e dar esse exercício como encerrado, mas ainda tem um bug que requer um pouco de atenção. O que aconteceria se, em vez de delimitarmos nosso saldo com uma vírgula (,) usássemos um ponto (por exemplo, 10.56)?

Exercício - Simulando Uma Conta Bancária Através Do Terminal

Isso acontece devido ao locale, a adaptação do input à cultura do local. Aqui no Brasil, o decimal é delimitado pela vírgula, então o método não entende que essa separação com ponto é válida.
A documentação da classe Scanner nos mostra que é possível alterar a cultura para uma que atenda ou um ou outro padrão, mas não os dois ao mesmo tempo. Também é possível alterar especificamente o delimitador, para um símbolo ou para outro, mas não os dois ao mesmo tempo.

Exercício - Simulando Uma Conta Bancária Através Do Terminal

Um dos métodos para solucionar esse problema não é muito elegante, mas resolve: em vez de capturar o dado diretamente como double, vamos usar o método Scanner.nextLine() para pegar o input como uma String, trocar os pontos por vírgula e tentar trocar o tipo para double.

public class TerminalAccount {  
    private int branch;  
    private String account;  
    private String clientName;  
    private double balance;  

    public void createAccount() {  
        Scanner sc = new Scanner(System.in);  

        //...
        boolean isBalanceInputCorrect = false;  
        do {  
            try {  
                System.out.print("Por favor, insira o saldo inicial: ");  
                String balanceString = sc.nextLine().replace(",", ".");  
                this.balance = Double.parseDouble(balanceString);  
                isBalanceInputCorrect = true;  
            } catch (NumberFormatException e) {  
                System.out.println("Por favor, insira apenas valores decimais.");  
            }  
        } while (!isBalanceInputCorrect);  

        //...

    }  
}

Além da alteração do método de captura do dado, tivemos mais algumas modificações: retiramos as chamadas para o método Scanner.nextLine() que serviam apenas para consumir os caracteres remanescentes, porque nosso input já faz isso pra gente. Além disso, o tipo do erro mudou: agora não se trata de um erro de incompatibilidade de tipo (InputMismatchException), mas sim um de erro no formato do número (NumberFormatException).
Com essas alterações feitas, bora ver se tudo deu certo:

Exercício - Simulando Uma Conta Bancária Através Do Terminal
Exercício - Simulando Uma Conta Bancária Através Do Terminal

Exercício - Simulando Uma Conta Bancária Através Do Terminal
Deu tudo certo! Com isso, conseguimos dizer que o exercício está concluído (finalmente)!
O repositório desse exercício está disponível aqui caso tenha interesse de ver.

E é isso. Até o próximo módulo!

版本聲明 本文轉載於:https://dev.to/lnabesima/exercicio-simulando-uma-conta-bancaria-atraves-do-terminal-23e4?1如有侵犯,請聯絡[email protected]刪除
最新教學 更多>
  • 如何使用替換指令在GO MOD中解析模塊路徑差異?
    如何使用替換指令在GO MOD中解析模塊路徑差異?
    在使用GO MOD時,在GO MOD 中克服模塊路徑差異時,可能會遇到衝突,其中可能會遇到一個衝突,其中3派對軟件包將另一個帶有導入套件的path package the Imptioned package the Imptioned package the Imported tocted pac...
    程式設計 發佈於2025-06-23
  • Python讀取CSV文件UnicodeDecodeError終極解決方法
    Python讀取CSV文件UnicodeDecodeError終極解決方法
    在試圖使用已內置的CSV模塊讀取Python中時,CSV文件中的Unicode Decode Decode Decode Decode decode Error讀取,您可能會遇到錯誤的錯誤:無法解碼字節 在位置2-3中:截斷\ uxxxxxxxx逃脫當CSV文件包含特殊字符或Unicode的路徑逃...
    程式設計 發佈於2025-06-23
  • 如何在Java字符串中有效替換多個子字符串?
    如何在Java字符串中有效替換多個子字符串?
    在java 中有效地替換多個substring,需要在需要替換一個字符串中的多個substring的情況下,很容易求助於重複應用字符串的刺激力量。 However, this can be inefficient for large strings or when working with nu...
    程式設計 發佈於2025-06-23
  • 您如何在Laravel Blade模板中定義變量?
    您如何在Laravel Blade模板中定義變量?
    在Laravel Blade模板中使用Elegance 在blade模板中如何分配變量對於存儲以後使用的數據至關重要。在使用“ {{}}”分配變量的同時,它可能並不總是最優雅的解決方案。 幸運的是,Blade通過@php Directive提供了更優雅的方法: $ old_section =...
    程式設計 發佈於2025-06-23
  • 在Python中如何創建動態變量?
    在Python中如何創建動態變量?
    在Python 中,動態創建變量的功能可以是一種強大的工具,尤其是在使用複雜的數據結構或算法時,Dynamic Variable Creation的動態變量創建。 Python提供了幾種創造性的方法來實現這一目標。 利用dictionaries 一種有效的方法是利用字典。字典允許您動態創建密鑰並...
    程式設計 發佈於2025-06-23
  • 如何在無序集合中為元組實現通用哈希功能?
    如何在無序集合中為元組實現通用哈希功能?
    在未訂購的集合中的元素要糾正此問題,一種方法是手動為特定元組類型定義哈希函數,例如: template template template 。 struct std :: hash { size_t operator()(std :: tuple const&tuple)const {...
    程式設計 發佈於2025-06-23
  • 如何使用不同數量列的聯合數據庫表?
    如何使用不同數量列的聯合數據庫表?
    合併列數不同的表 當嘗試合併列數不同的數據庫表時,可能會遇到挑戰。一種直接的方法是在列數較少的表中,為缺失的列追加空值。 例如,考慮兩個表,表 A 和表 B,其中表 A 的列數多於表 B。為了合併這些表,同時處理表 B 中缺失的列,請按照以下步驟操作: 確定表 B 中缺失的列,並將它們添加到表的...
    程式設計 發佈於2025-06-23
  • CSS強類型語言解析
    CSS強類型語言解析
    您可以通过其强度或弱输入的方式对编程语言进行分类的方式之一。在这里,“键入”意味着是否在编译时已知变量。一个例子是一个场景,将整数(1)添加到包含整数(“ 1”)的字符串: result = 1 "1";包含整数的字符串可能是由带有许多运动部件的复杂逻辑套件无意间生成的。它也可以是故意从单个真理...
    程式設計 發佈於2025-06-23
  • 在JavaScript中如何並發運行異步操作並正確處理錯誤?
    在JavaScript中如何並發運行異步操作並正確處理錯誤?
    同意操作execution 在執行asynchronous操作時,相關的代碼段落會遇到一個問題,當執行asynchronous操作:此實現在啟動下一個操作之前依次等待每個操作的完成。要啟用並發執行,需要進行修改的方法。 第一個解決方案試圖通過獲得每個操作的承諾來解決此問題,然後單獨等待它們: c...
    程式設計 發佈於2025-06-23
  • 如何使用node-mysql在單個查詢中執行多個SQL語句?
    如何使用node-mysql在單個查詢中執行多個SQL語句?
    在node-mysql node-mysql文檔最初出於安全原因最初禁用多個語句支持,因為它可能導致SQL注入攻擊。要啟用此功能,您需要在創建連接時將倍增設置設置為true: var connection = mysql.createconnection({{multipleStatement:...
    程式設計 發佈於2025-06-23
  • 如何使用PHP將斑點(圖像)正確插入MySQL?
    如何使用PHP將斑點(圖像)正確插入MySQL?
    essue VALUES('$this->image_id','file_get_contents($tmp_image)')";This code builds a string in PHP, but the function call fil...
    程式設計 發佈於2025-06-23
  • 如何使用組在MySQL中旋轉數據?
    如何使用組在MySQL中旋轉數據?
    在關係數據庫中使用mySQL組使用mySQL組進行查詢結果,在關係數據庫中使用MySQL組,轉移數據的數據是指重新排列的行和列的重排以增強數據可視化。在這裡,我們面對一個共同的挑戰:使用組的組將數據從基於行的基於列的轉換為基於列。 Let's consider the following ...
    程式設計 發佈於2025-06-23
  • 如何正確使用與PDO參數的查詢一樣?
    如何正確使用與PDO參數的查詢一樣?
    在pdo 中使用類似QUERIES在PDO中的Queries時,您可能會遇到類似疑問中描述的問題:此查詢也可能不會返回結果,即使$ var1和$ var2包含有效的搜索詞。錯誤在於不正確包含%符號。 通過將變量包含在$ params數組中的%符號中,您確保將%字符正確替換到查詢中。沒有此修改,PD...
    程式設計 發佈於2025-06-23
  • Java的Map.Entry和SimpleEntry如何簡化鍵值對管理?
    Java的Map.Entry和SimpleEntry如何簡化鍵值對管理?
    A Comprehensive Collection for Value Pairs: Introducing Java's Map.Entry and SimpleEntryIn Java, when defining a collection where each element com...
    程式設計 發佈於2025-06-23
  • 如何克服PHP的功能重新定義限制?
    如何克服PHP的功能重新定義限制?
    克服PHP的函數重新定義限制在PHP中,多次定義一個相同名稱的函數是一個no-no。嘗試這樣做,如提供的代碼段所示,將導致可怕的“不能重新列出”錯誤。 但是,PHP工具腰帶中有一個隱藏的寶石:runkit擴展。它使您能夠靈活地重新定義函數。 runkit_function_renction_...
    程式設計 發佈於2025-06-23

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

Copyright© 2022 湘ICP备2022001581号-3