Entre para a LISTA VIP da Black Friday

00

DIAS

00

HORAS

00

MIN

00

SEG

Clique para saber mais
Alura > Cursos de Programação > Cursos de Java > Conteúdos de Java > Primeiras aulas do curso Java e refatoração: melhorando códigos com boas práticas

Java e refatoração: melhorando códigos com boas práticas

Refatoração - Apresentação

Olá, meu nome é João Vitor e serei seu instrutor no curso de refatoração utilizando Java.

Audiodescrição: João Victor é uma pessoa de pele clara, olhos castanhos e cabelos pretos curtos. Usa bigode e barba. Está com uma camiseta escura e sentado em uma cadeira preta. Ao fundo, uma parede lisa com iluminação em tons de azul.

Refatorando com Java

Caso deseje aprender a transformar um código onde temos somente a uma função em um código com boas práticas e testes, este é o lugar certo para você.

No exemplo exibido na tela, temos a função Main(), na qual se encontra todo o código com todas as regras de negócio e configurações. Nesse curso, transformaremos um código de função única em um código refatorado com organização em classes, pacotes e testes aplicados.

A classe, que antes acumulava todas as regras de negócio e configurações, se tornará mais enxuta e passará a utilizar o Switch, presente nas versões mais recentes do Java.

Veremos a divisão de classes com a utilização do Padrão de Projeto Command, com o intuito de fazer a requisição para as classes e métodos corretos no código. Também teremos classes específicas para configurações, onde nós utilizaremos uma API REST, fazendo chamadas GET e POST.

Separaremos classes específicas para cada tipo e faremos testes de unidade utilizando JUnit e Mockito.

Dessa forma, seremos capazes de aplicar diversos padrões e boas práticas para obter um código coeso e de fácil manutenção, tornando o seu desenvolvimento uma tarefa mais gratificante.

Se sua curiosidade foi instigada e deseja aprender a aplicar essas boas práticas em seu dia a dia, te esperamos no próximo vídeo.

Refatoração - Analisando o código

Precisamos conhecer a aplicação em que vamos trabalhar ao longo deste curso.

Conhecendo a Aplicação

A ideia desta aplicação é ser a ferramenta que as pessoas usuárias de nosso controle de adoção utilizarão para cadastrar abrigos, registrar animais para adoção e listá-los. Ou seja, é uma aplicação console voltada para a pessoa que trabalha manuseando o sistema de cadastro de animais e abrigos.

Acessando o IntelliJ e abrindo o arquivo do projeto, veremos no explorador de arquivos à esquerda que a aplicação é composta de uma única classe, chamada AdopetConsoleApplication, dentro do caminho de pastas "src > main > java > br.com.alura".

Vamos minimizar esse explorador clicando no ícone "-" chamado "Hide" (Esconder), localizado acima dele, alinhado à direita. Assim, podemos focar mais no código.

Após abrir o arquivo, veremos uma aplicação console tem uma função principal, ou main(). É por meio dela que iniciamos a função e a utilizamos para interagir com o sistema através do console.

Entre as chaves dessa função, temos um bloco while com algumas opções para interação:

Abaixo dessas opções, temos um if para quando selecionamos a opção 1, que faz o sistema realizar uma chamada para uma API externa utilizando um método get() para buscar e mostrar os abrigos cadastrados. Com isso, estamos consumindo uma API.

Abaixo dessa, temos outras opções. Em algumas delas, faremos uma requisição do tipo post(). Com isso, quando a pessoa usuária cadastrar um abrigo, na verdade, não estaremos registrando na nossa aplicação console, pois o sistema não tem um banco de dados. Em vez disso, faremos uma requisição post() para uma API responsável pelo controle de todos os dados.

Quando quisermos buscar dados, faremos um get() para a API e quando quisermos registrar dados, faremos um post() para a API.

Neste arquivo, as várias funções no código representam as várias opções de interação na aplicação console e formam o código completo da aplicação.

Para iniciar a aplicação console, no caso do IntelliJ que utilizado no curso, basta clicar no ícone de "Play" ou "Start" na cor verde, à esquerda da linha inicial do public static void main(). Outras IDEs possuem funcionalidades semelhantes.

Vamos clicar nesse ícone para selecionar a opção "Run AdopetConsoleApplication" na lista suspensa e iniciar a aplicação.

O resultado é a abertura do terminal na aba inferior da IDE com a solicitação abaixo:

DIGITE O NÚMERO DA OPERAÇÃO DESEJADA:

1 -> Listar abrigos cadastrados

2 -> Cadastrar novo abrigo

3 -> Listar pets do abrigo

4 -> Importar pets do abrigo

5 -> Sair

Para interagir com a aplicação, basta selecionar uma opção. Neste momento não temos nenhum abrigo cadastrado, portanto digitaremos "5" para fechar a aplicação e fecharemos o terminal.

A aplicação precisa da inserção de dados para cadastro na API. Dessa forma, quando realizarmos uma requisição, buscaremos nela os dados cadastrados.

Bloco de código completo do arquivo AdopetConsoleApplication.java no Github.

Encerrando o programa, podemos ver que temos um else if para cada opção. No caso da importação de pets (animais de estimação), por exemplo, é necessário passar o nome do abrigo e o nome do arquivo CSV.

Verificando o explorador lateral, temos um arquivo pets.csv disponível na pasta raiz, mas poderia ser qualquer outro. Com essas informações, a aplicação fará a leitura do arquivo e cadastrará esses pets na API, associando-os ao abrigo que informamos.

Percebemos que a aplicação está funcionando, mas o que importa mesmo é o código por trás dela. Se formos analisar, temos uma única função, um único método main() que contém muitas linhas de código. Navegando pelo código, podemos ver que ele é longo e dificulta a identificação dos blocos de código.

Neste cenário, se quiséssemos incrementar a aplicação com uma nova função, teríamos que adicionar mais um bloco else if, dificultando ainda mais a manutenção e a melhoria do código.

Assim, o que vamos começar a fazer é refatorar esse código. A refatoração (refactor) consiste em técnicas de melhoria para o nosso código. Existem várias maneiras de fazer, diversos padrões e técnicas que podemos aplicar. Neste projeto, vamos aplicar apenas algumas delas, porque realmente o nosso código está difícil de ser trabalhado.

Iniciando a Refatoração: Criando a Função de Listagem

Uma das abordagens é dividir esse método main() em pequenas funções. Por exemplo, por que não termos um método para cada opção, em vez de possuir todo o código e lógica dentro de condições if? Dessa forma, todo o código que atualmente manipulamos com HttpClient, response e um laço for, ficaria dentro do seu próprio método.

Portanto, podemos criar uma função chamada listarAbrigos() porque, de fato, a opção que executa essa lógica será a função de listar os abrigos.

Vamos acessar o final do código do programa, à direita do fechamento da penúltima chave, pressionar "Enter" duas vezes para descer duas linhas e adicionar um espaço após essa chave. Em seguida, criaremos uma função privada (private).

Vale lembrar que essa chave se refere à função main(), enquanto a última chave é usada para finalizar o programa e encerrar o bloco da classe.

Então, o que será essa função? Será designada como private void, como a função chamada cadastrarAbrigo(). À sua direita, abriremos e fecharemos chaves para delimitá-la.

private void cadastrarAbrigo() {

}

Agora, voltaremos para o código da função main(). Observamos que cadastrarAbrigos() funcionará como a opção 1. Em outras palavras, a opção 1 inicia no primeiro if, logo abaixo de opcaoescolhida = Integer.parseInt(textoDigitado).

if (opcaoEscolhida == 1) {
    HttpClient client = HttpClient.newHttpClient();
    String uri = "http://localhost:8080/abrigos";
    HttpRequest request = HttpRequest.newBuilder()
                            .uri(URI.create(uri))
                            .method("GET", HttpRequest.BodyPublishers.noBody())
                            .build();
    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    String responseBody = response.body();
    JsonArray jsonArray = JsonParser.parseString(responseBody).getAsJsonArray();
    System.out.println("Abrigos cadastrados:");
    for (JsonElement element : jsonArray) {
        JsonObject jsonObject = element.getAsJsonObject();
        long id = jsonObject.get("id").getAsLong();
        String nome = jsonObject.get("nome").getAsString();
        System.out.println(id +" - " +nome);
    }
}

Portanto, se a opção escolhida for igual a 1, todo o código dentro do if será executado. Quando clicamos à direita da abertura de chaves desse if, é possível ver em qual parte do código essa seção é encerrada. Todo o código dentro desse if é o que será movido para o novo método.

Assim, vamos recortar com "Ctrl+X" todo o trecho contido entre as chaves desse if, que se inicia a partir da linha do HttpClient client e termina na chave que fecha o for. Voltando para a função private que acabamos de criar, colamos o conteúdo recortado entre as chaves de cadastrarAbrigo.

private void cadastrarAbrigo() {
    HttpClient client = HttpClient.newHttpClient();
    String uri = "http://localhost:8080/abrigos";
    HttpRequest request = HttpRequest.newBuilder()
                            .uri(URI.create(uri))
                            .method("GET", HttpRequest.BodyPublishers.noBody())
                            .build();
    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    String responseBody = response.body();
    JsonArray jsonArray = JsonParser.parseString(responseBody).getAsJsonArray();
    System.out.println("Abrigos cadastrados:");
    for (JsonElement element : jsonArray) {
        JsonObject jsonObject = element.getAsJsonObject();
        long id = jsonObject.get("id").getAsLong();
        String nome = jsonObject.get("nome").getAsString();
        System.out.println(id +" - " +nome);
    }
}

O sistema aponta um erro em send, porque precisa indicar que esse método pode gerar uma exceção Java IOException ou uma InterruptedException. Precisamos explicitar que esse erro pode ocorrer — tratando com um bloco try catch, por exemplo — para demonstrar que podemos lidar com essa exceção (caso ocorra) ou podemos deixar claro que, neste método, haverá um passo — neste caso, o send() — que pode gerar estas duas exceções.

Ou seja, nessas Checked Exceptions, precisamos deixar evidente que as exceções podem ocorrer.

Por isso, vamos incorporar um throws no send(request, HttpResponse.BodyHandlers.ofString()). Para isso, clicaremos na opção "Add exceptions to method signature" (Adicionar exceções à assinatura do método), dentro da janela desse erro apontado pelo sistema. Não trabalharemos com try catch.

private void cadastrarAbrigo() throws IOException, InterruptedException {
    // Código omitido
}

O próximo passo é copiar o nome da função cadastrarAbrigo(), e inseri-lo entre as chaves do if.

if (opcaoEscolhida == 1) {
    cadastrarAbrigo()
}

Um erro será apontado nessa função porque, enquanto trabalhamos na função main() como uma função estática, precisamos tratar sempre no contexto estático. Então, a função cadastrarAbrigo() precisa ser designada como private static void.

private static void cadastrarAbrigo() throws IOException, InterruptedException {
    // Código omitido
}

Ao retornarmos para a parte superior do código, verificamos que a função está sendo chamada com sucesso, sem o erro de compilação, que seria indicado por uma linha vermelha abaixo do método.

A ideia é separar todas as funções em métodos para diminuir o código da função main() e deixar responsabilidades e comportamentos em métodos separados.

Neste vídeo, introduzimos o contexto da aplicação e iniciamos a refatoração, vamos concluir esses passos no próximo vídeo. Esperamos você lá!

Refatoração - Extraindo métodos

Agora que extraímos o primeiro método para tornar nosso código um pouco mais legível, precisamos continuar extraindo todos os blocos de código da função main() que podem se tornar métodos.

Porém, antes disso, gostaria que corrigíssemos uma pequena coisa em nosso código.

Anteriormente, nós criamos o método cadastrarAbrigo(), que será a opção número 1. Na verdade, a opção 1 não é cadastrarAbrigo(), e sim listarAbrigo(). Se olharmos no bloco while, dentro da função main(), no System.out.println(), a opção 1 é "Listar abrigos cadastrados".

Para resolver esse problema, precisamos rolar a tela até a nossa função de cadastrarAbrigo() e mudaremos o seu nome de cadastrar para listar.

private static void listarAbrigo() throws IOException, InterruptedException {
    // Código omitido
}

Feito isso, vamos copiar o listarAbrigo() com "Ctrl+C" e referenciá-lo lá em cima, onde adicionaremos cadastrarAbrigo(), porque agora o nosso código parou de compilar, ele não encontra essa função.

if (opcaoEscolhida == 1) {
    listarAbrigo();
}

Extraindo os Métodos

Agora, precisamos criar o método para a opção 2 que é, de fato, cadastrarAbrigo(). Voltaremos ao fim do arquivo e, abaixo da função listarAbrigo(), criaremos a cadastrarAbrigo().

Sabemos que precisamos criar uma função privada (private), pois estamos trabalhando no mesmo arquivo. Dentro do mesmo arquivo, podemos usar o modificador private para referenciar na mesma classe. Este método precisa ser estático (static), pois estamos chamando através de uma função main() que é estática.

Ela não tem retorno, então será void. Por fim, adicionaremos o nome cadastrarAbrigo, abriremos um bloco de parênteses (())e também um bloco de chaves ({}).

private static void cadastrarAbrigo(){
    
}

Agora, vamos voltar à função main() para extrair o código do else if que contém a opcaoEscolhida == 2, recortando o trecho que se inicia no System.out.println() da frase "Digite o nome do abrigo", e termina no fechamento das chaves do bloco else if que devolve a mensagem "Erro ao cadastrar o abrigo".

}else if (opcaoEscolhida == 2) {
    System.out.println("Digite o nome do abrigo:");
    String nome = new Scanner(System.in).nextLine();
    System.out.println("Digite o telefone do abrigo:");
    String telefone = new Scanner(System.in).nextLine();
    System.out.println("Digite o email do abrigo:");
    String email = new Scanner(System.in).nextLine();

    JsonObject json = new JsonObject();
    json.addProperty("nome", nome);
    json.addProperty("telefone", telefone);
    json.addProperty("email", email);

    HttpClient client = HttpClient.newHttpClient();
    String uri = "http://localhost:8080/abrigos";
    HttpRequest request = HttpRequest.newBuilder()                            .uri(URI.create(uri))
         .header("Content-Type", "application/json")
         .method("POST", HttpRequest.BodyPublishers.ofString(json.toString()))
         .build();

    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    int statusCode = response.statusCode();
    String responseBody = response.body();
    if (statusCode == 200) {
        System.out.println("Abrigo cadastrado com sucesso!");
        System.out.println(responseBody);
    } else if (statusCode == 400 || statusCode == 500) {
        System.out.println("Erro ao cadastrar o abrigo:");
        System.out.println(responseBody);
    }
}

Voltaremos à função cadastrarAbrigo() que criamos no final da nossa classe e colaremos o conteúdo recortado entre suas chaves.

private static void cadastrarAbrigo(){
    System.out.println("Digite o nome do abrigo:");
    String nome = new Scanner(System.in).nextLine();
    System.out.println("Digite o telefone do abrigo:");
    String telefone = new Scanner(System.in).nextLine();
    System.out.println("Digite o email do abrigo:");
    String email = new Scanner(System.in).nextLine();

    JsonObject json = new JsonObject();
    json.addProperty("nome", nome);
    json.addProperty("telefone", telefone);
    json.addProperty("email", email);

    HttpClient client = HttpClient.newHttpClient();
    String uri = "http://localhost:8080/abrigos";
    HttpRequest request = HttpRequest.newBuilder()                            .uri(URI.create(uri))
         .header("Content-Type", "application/json")
         .method("POST", HttpRequest.BodyPublishers.ofString(json.toString()))
         .build();

    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    int statusCode = response.statusCode();
    String responseBody = response.body();
    if (statusCode == 200) {
        System.out.println("Abrigo cadastrado com sucesso!");
        System.out.println(responseBody);
    } else if (statusCode == 400 || statusCode == 500) {
        System.out.println("Erro ao cadastrar o abrigo:");
        System.out.println(responseBody);
    }
}

Notaremos que o send(), do nosso método não está compilando, ou seja, está com uma linha vermelha embaixo dele.

O método send() pode lançar uma exceção checada (Checked Exception). Conforme vimos anteriormente, esta é uma exceção que precisamos tratar através de um try catch ou deixar explícito na assinatura do método que ela pode ser lançada. Se não fizermos isso, nossa classe não vai compilar.

Vamos colocar o cursor sobre o send(), a IDE - nesse caso, o IntelliJ — exibir uma caixa dde diálogo com a opção de adicionar as exceções checadas na assinatura do método ("Add exceptions to method signature").

Ao selecioná-la, ele vai adicionar um throws IOException, InterruptedException para deixar explícito na primeira linha do método cadastrarAbrigo() que este pode lançar uma exceção IOExceptional ou InterruptedExceptional que não estamos tratando através de um try-catch.

private static void cadastrarAbrigo() throws IOException, InterruptedException {
    System.out.println("Digite o nome do abrigo:");
    String nome = new Scanner(System.in).nextLine();
    System.out.println("Digite o telefone do abrigo:");
    String telefone = new Scanner(System.in).nextLine();
    System.out.println("Digite o email do abrigo:");
    String email = new Scanner(System.in).nextLine();

    JsonObject json = new JsonObject();
    json.addProperty("nome", nome);
    json.addProperty("telefone", telefone);
    json.addProperty("email", email);

    HttpClient client = HttpClient.newHttpClient();
    String uri = "http://localhost:8080/abrigos";
    HttpRequest request = HttpRequest.newBuilder()                            .uri(URI.create(uri))
         .header("Content-Type", "application/json")
         .method("POST", HttpRequest.BodyPublishers.ofString(json.toString()))
         .build();

    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    int statusCode = response.statusCode();
    String responseBody = response.body();
    if (statusCode == 200) {
        System.out.println("Abrigo cadastrado com sucesso!");
        System.out.println(responseBody);
    } else if (statusCode == 400 || statusCode == 500) {
        System.out.println("Erro ao cadastrar o abrigo:");
        System.out.println(responseBody);
    }
}

Por fim, vamos referenciar o cadastrarAbrigo() no método main(), entre as chaves da opcaoEscolhida == 2.

public class AdopetConsoleApplication {

    public static void main(String[] args) {
        // Código omitido

                if (opcaoEscolhida == 1) {
                    listarAbrigo();
                } else if (opcaoEscolhida == 2) {
                    cadastrarAbrigo();
                }
        // Código omitido

}

Com isso, temos mais uma função. Agora temos uma função para a opção 1 e outra para a opção 2.

Vamos prosseguir, extraindo todo o código que está dentro do else if da opção 3 para uma nova função. Nesse caso, a função é "Listar pets do abrigo". Portanto, o nome da nossa função será listarPetsDoAbrigo().

Voltaremos ao final da nossa classe para criar a nova função private static void listarPetsDoAbrigo() sem nenhum parâmetro. à sua direita, abriremos e fecharemos chaves.

private static void listarPetsDoAbrigo(){
    
}

Voltando à função main(), extrairemos com "Ctrl+C" o código do bloco else if da opção 3 para migrá-lo para a nova função. O bloco inicia no System.out.println() da mensagem "Digite o id ou nome do abrigo" e termina antes do else if da opção 4.

Voltaremos para a função listarPetsDoAbrigo() que acabamos de criar e pressionaremos Ctrl V para colar o código.

private static void listarPetsDoAbrigo(){
    System.out.println("Digite o id ou nome do abrigo:");
    String idOuNome = new Scanner(System.in).nextLine();

    HttpClient client = HttpClient.newHttpClient();
    String uri = "http://localhost:8080/abrigos/" +idOuNome +"/pets";
    HttpRequest request = HttpRequest.newBuilder()
           .uri(URI.create(uri))
           .method("GET", HttpRequest.BodyPublishers.noBody())
           .build();
    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    int statusCode = response.statusCode();
    if (statusCode == 404 || statusCode == 500) {
        System.out.println("ID ou nome não cadastrado!");
    }
    String responseBody = response.body();
    JsonArray jsonArray = JsonParser.parseString(responseBody).getAsJsonArray();
    System.out.println("Pets cadastrados:");
    for (JsonElement element : jsonArray) {
        JsonObject jsonObject = element.getAsJsonObject();
        long id = jsonObject.get("id").getAsLong();
        String tipo = jsonObject.get("tipo").getAsString();
        String nome = jsonObject.get("nome").getAsString();
        String raca = jsonObject.get("raca").getAsString();
        int idade = jsonObject.get("idade").getAsInt();
        System.out.println(id +" - " +tipo +" - " +nome +" - " +raca +" - " +idade +" ano(s)");
    }
}

Vamos notar que o send() exibirá o mesmo erro acusado pela IDE com um traço vermelho abaixo do código. Para evitar isso, adicionaremos as exceções na assinatura do método.

private static void listarPetsDoAbrigo() throws IOException, InterruptedException {
    System.out.println("Digite o id ou nome do abrigo:");
    String idOuNome = new Scanner(System.in).nextLine();

    HttpClient client = HttpClient.newHttpClient();
    String uri = "http://localhost:8080/abrigos/" +idOuNome +"/pets";
    HttpRequest request = HttpRequest.newBuilder()
           .uri(URI.create(uri))
           .method("GET", HttpRequest.BodyPublishers.noBody())
           .build();
    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    int statusCode = response.statusCode();
    if (statusCode == 404 || statusCode == 500) {
        System.out.println("ID ou nome não cadastrado!");
    }
    String responseBody = response.body();
    JsonArray jsonArray = JsonParser.parseString(responseBody).getAsJsonArray();
    System.out.println("Pets cadastrados:");
    for (JsonElement element : jsonArray) {
        JsonObject jsonObject = element.getAsJsonObject();
        long id = jsonObject.get("id").getAsLong();
        String tipo = jsonObject.get("tipo").getAsString();
        String nome = jsonObject.get("nome").getAsString();
        String raca = jsonObject.get("raca").getAsString();
        int idade = jsonObject.get("idade").getAsInt();
        System.out.println(id +" - " +tipo +" - " +nome +" - " +raca +" - " +idade +" ano(s)");
    }
}

Copiaremos o nome da nossa função, listarPetsDoAbrigo(), subiremos ao interior das chaves do else if que continha o código recortado e configuraremos a opção 3 pela referência à função.

public class AdopetConsoleApplication {

    public static void main(String[] args) {
        // Código omitido

                if (opcaoEscolhida == 1) {
                    listarAbrigo();
                } else if (opcaoEscolhida == 2) {
                    cadastrarAbrigo();
                } else if (opcaoEscolhida == 3){
                    listarPetsDoAbrigo()
                }
        // Código omitido

}

Estamos encurtando a função main(), que estava muito longa e cheia de código. Para isso, damos comportamentos específicos para a nossa classe: uma para listar pets, outra para listar abrigos e outra para cadastro. Esse é o nosso objetivo.

Por último, temos a opção 4, "Importar pets do abrigo". Essa função lê o arquivo pets.csv, mas poderíamos referenciar outros. Ao importar esse arquivo, o sistema lerá todo o seu conteúdo e registrará na nossa API.

Vamos criar uma nova função, private static void importarPetsDoAbrigo() sem parâmetros, abaixo das funções anteriores.

private static void importarPetsDoAbrigo() {

}

Copiaremos todo o código do else if da opção 4 e colaremos entre as chaves da nova função importarPetsDoAbrigo(). Esse bloco se inicia no System.out.println() da frase "Digite o id ou nome do abrigo" e termina na linha reader.close() antes do bloco da opção 5.

private static void importarPetsDoAbrigo() {
    System.out.println("Digite o id ou nome do abrigo:");
    String idOuNome = new Scanner(System.in).nextLine();

    System.out.println("Digite o nome do arquivo CSV:");
    String nomeArquivo = new Scanner(System.in).nextLine();

    BufferedReader reader;
    try {
        reader = new BufferedReader(new FileReader(nomeArquivo));
    } catch (IOException e) {
        System.out.println("Erro ao carregar o arquivo: " + nomeArquivo);
        continue;
    }
    String line;
    while ((line = reader.readLine()) != null) {
        String[] campos = line.split(",");
        String tipo = campos[0];
        String nome = campos[1];
        String raca = campos[2];
        int idade = Integer.parseInt(campos[3]);
        String cor = campos[4];
        Float peso = Float.parseFloat(campos[5]);

        JsonObject json = new JsonObject();
        json.addProperty("tipo", tipo.toUpperCase());
        json.addProperty("nome", nome);
        json.addProperty("raca", raca);
        json.addProperty("idade", idade);
        json.addProperty("cor", cor);
        json.addProperty("peso", peso);

        HttpClient client = HttpClient.newHttpClient();
        String uri = "http://localhost:8080/abrigos/" + idOuNome + "/pets";
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(uri))
                .header("Content-Type", "application/json")
                .method("POST", HttpRequest.BodyPublishers.ofString(json.toString()))
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        int statusCode = response.statusCode();
        String responseBody = response.body();
        if (statusCode == 200) {
            System.out.println("Pet cadastrado com sucesso: " + nome);
        } else if (statusCode == 404) {
            System.out.println("Id ou nome do abrigo não encontrado!");
            break;
        } else if (statusCode == 400 || statusCode == 500) {
            System.out.println("Erro ao cadastrar o pet: " + nome);
            System.out.println(responseBody);
            break;
        }
    }
    reader.close();
}

Temos a mesma questão anterior com o método close(), ele pode lançar uma exceção do tipo IOException. Vamos selecionar a opção "Add exception to method signature" para adicionar essa exceção à assinatura do método.

O send() também pode causar erro, conforme observamos na função anterior. Portanto, adicionaremos também as duas exceções que podem ser exibidas nele.

private static void importarPetsDoAbrigo() throws IOException, InterruptedException {
    System.out.println("Digite o id ou nome do abrigo:");
    String idOuNome = new Scanner(System.in).nextLine();

    System.out.println("Digite o nome do arquivo CSV:");
    String nomeArquivo = new Scanner(System.in).nextLine();

    BufferedReader reader;
    try {
        reader = new BufferedReader(new FileReader(nomeArquivo));
    } catch (IOException e) {
        System.out.println("Erro ao carregar o arquivo: " + nomeArquivo);
        continue;
    }
    String line;
    while ((line = reader.readLine()) != null) {
        String[] campos = line.split(",");
        String tipo = campos[0];
        String nome = campos[1];
        String raca = campos[2];
        int idade = Integer.parseInt(campos[3]);
        String cor = campos[4];
        Float peso = Float.parseFloat(campos[5]);

        JsonObject json = new JsonObject();
        json.addProperty("tipo", tipo.toUpperCase());
        json.addProperty("nome", nome);
        json.addProperty("raca", raca);
        json.addProperty("idade", idade);
        json.addProperty("cor", cor);
        json.addProperty("peso", peso);

        HttpClient client = HttpClient.newHttpClient();
        String uri = "http://localhost:8080/abrigos/" + idOuNome + "/pets";
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(uri))
                .header("Content-Type", "application/json")
                .method("POST", HttpRequest.BodyPublishers.ofString(json.toString()))
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        int statusCode = response.statusCode();
        String responseBody = response.body();
        if (statusCode == 200) {
            System.out.println("Pet cadastrado com sucesso: " + nome);
        } else if (statusCode == 404) {
            System.out.println("Id ou nome do abrigo não encontrado!");
            break;
        } else if (statusCode == 400 || statusCode == 500) {
            System.out.println("Erro ao cadastrar o pet: " + nome);
            System.out.println(responseBody);
            break;
        }
    }
    reader.close();
}

Precisamos resolver outro problema. Dentro do while do nosso novo método, o reader() não está sendo reconhecido. Se posicionarmos o cursor em cima dele, veremos uma mensagem da IDE informando que precisamos inicializar a variável reader, declarada com o tipo BufferedReader, abaixo da linha String nomeArquivo.

Com a mudança do método main(), precisaremos instanciar a variável reader. Vamos inicializá-la como nula, porque quando o tryabaixo dela for acionado, o reader será instanciado e dentro do while, teremos esse objeto.

BufferedReader reader = null;

O bloco catch abaixo da variável reader não está compilando, pois o continue está indicado pelo sublinhado vermelho. Vamos verificar o que está acontecendo.

Quando esse código estava dentro do main(), estava dentro de um loop (laço) while. A ideia era que, se ocorresse qualquer erro no momento da importação de um arquivo, ele registraria um erro ao carregar o arquivo e continuaria nas opções.

Porém, após extrairmos o código do laço, o continue perdeu o seu sentido, já que não estamos mais dentro de um ciclo. Portanto, vamos removê-lo, mas ainda precisamos pensar em uma estratégia para corrigir isso, já que se caírmos no catch, não voltaremos para o menu.

BufferedReader reader = null;
try {
    reader = new BufferedReader(new FileReader(nomeArquivo));
} catch (IOException e) {
    System.out.println("Erro ao carregar o arquivo: " + nomeArquivo);
}

O código completo da função importarPetsDoAbrigo() pode ser vista abaixo.

private static void importarPetsDoAbrigo() throws IOException, InterruptedException {
    System.out.println("Digite o id ou nome do abrigo:");
    String idOuNome = new Scanner(System.in).nextLine();

    System.out.println("Digite o nome do arquivo CSV:");
    String nomeArquivo = new Scanner(System.in).nextLine();

    BufferedReader reader = null;
    try {
        reader = new BufferedReader(new FileReader(nomeArquivo));
    } catch (IOException e) {
        System.out.println("Erro ao carregar o arquivo: " + nomeArquivo);
    }
    String line;
    while ((line = reader.readLine()) != null) {
        String[] campos = line.split(",");
        String tipo = campos[0];
        String nome = campos[1];
        String raca = campos[2];
        int idade = Integer.parseInt(campos[3]);
        String cor = campos[4];
        Float peso = Float.parseFloat(campos[5]);

        JsonObject json = new JsonObject();
        json.addProperty("tipo", tipo.toUpperCase());
        json.addProperty("nome", nome);
        json.addProperty("raca", raca);
        json.addProperty("idade", idade);
        json.addProperty("cor", cor);
        json.addProperty("peso", peso);

        HttpClient client = HttpClient.newHttpClient();
        String uri = "http://localhost:8080/abrigos/" + idOuNome + "/pets";
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(uri))
                .header("Content-Type", "application/json")
                .method("POST", HttpRequest.BodyPublishers.ofString(json.toString()))
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        int statusCode = response.statusCode();
        String responseBody = response.body();
        if (statusCode == 200) {
            System.out.println("Pet cadastrado com sucesso: " + nome);
        } else if (statusCode == 404) {
            System.out.println("Id ou nome do abrigo não encontrado!");
            break;
        } else if (statusCode == 400 || statusCode == 500) {
            System.out.println("Erro ao cadastrar o pet: " + nome);
            System.out.println(responseBody);
            break;
        }
    }
    reader.close();
}

Esse não é o nosso objetivo atual, mas é algo que precisamos considerar. O foco era extrair todos os métodos e códigos do main(), e nisso fomos bem sucedidos.

Agora, vamos inicializar a aplicação e conferir se está tudo correto.

No explorador lateral, veremos na pasta do projeto que temos a API api.jar que executa todas as operações através do nosso console público. Podemos instanciá-la utilizando o nosso terminal.

Abriremos o terminal na aba inferior do IntelliJ, na qual digitaremos e rodaremos o comando abaixo, onde api.jar é o nome do arquivo. Com isso, ele vai instanciar e inicializar a nossa API. Agora, posso minimizar esse terminal, clicando no ícone de minimizar no canto superior direito da aba.

java -jar api.jar

Voltando à função main(), vamos clicar no ícone de "Play" à esquerda para inicializar o projeto. Com isso, veremos que mesmo com todas as mudanças e extraindo métodos, o comportamento da aplicação não mudou.

Por exemplo, se quisermos listar um abrigo cadastrado, selecionando a opção "1" no terminal e pressionar "Enter", ele retornará a mensagem "Abrigos cadastrados" e um campo vazio. Esse é o mesmo comportamento que tínhamos anteriormente.

Se escolhermos a opção 3, ele solicitará o ID ou o nome do abrigo. Como não temos nenhum abrigo cadastrado, vamos fornecer o ID '1', como exemplo. A resposta será nula, pois não temos nenhum animal de estimação cadastrado.

A seguir, precisamos tratar nossa aplicação para que estes comportamentos sejam normalizados, no sentido de termos dados para trabalhar. Por exemplo, toda vez que listarmos os animais de estimação, ele deve retornar os animais cadastrados.

Vimos em um momento que ele não retornou dados e consequentemente, exibiu uma exceção. Este não é um comportamento que desejamos em nossa aplicação.

Como podemos criar um comportamento que seja mais amigável e comum na nossa aplicação? Isso será feito à medida que avançarmos nas aulas e refatorarmos nosso código.

Sobre o curso Java e refatoração: melhorando códigos com boas práticas

O curso Java e refatoração: melhorando códigos com boas práticas possui 224 minutos de vídeos, em um total de 43 atividades. Gostou? Conheça nossos outros cursos de Java em Programação, ou leia nossos artigos de Programação.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Java acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas