Muitos dizem que NullPointerException é um erro comum de iniciantes, o que é verdade. Porém, muitos desenvolvedores experientes também encontram o temido NPE em seus códigos. Isso porque não utilizam boas práticas de desenvolvimento para evitar tal tipo de transtorno.

Nesse post tentarei abordar algumas boas práticas de programação que irão acabar com esse erro corriqueiro e, se bem praticadas, você esquecerá que esse erro existe! E de lambuja ainda vai conseguir escrever códigos mais claros e mais orientados a objeto.

Good citizen

A primeira é a utilização do pattern Good Citizen (Bom Cidadão) que agrupa algumas boas práticas interessantes que nos ajudam a manter nosso objeto consistente, respeitando regras para uma boa cidadania na sociedade de classes, ou seja, permanecendo em um estado adequado e se comportando de maneira correta e elegante dentro do código.

Alguns pontos que esse padrão aborda são:

  1. Manter estado consistente em todos os momentos.
  2. Quando criamos um objeto, devemos inicializá-lo por completo, passando todos os parâmetros pelo construtor. Se inicializarmos os com valores válidos, nunca teremos variáveis nulas!
  3. Nunca espere ou retorne nulo
    Até o criador do conceito de nulo considera que errou ao criá-lo null.
  4. Falha rápida: não prosseguir se o objeto está inconsistente
    Seu objeto deve validar se o seu próprio estado está consistente. Ou alguém deverá validá-lo. Não devemos deixar que objetos inconsistentes trafeguem pela aplicação causando efeitos colaterais no sistema.
  5. Imutabilidade
    Objetos são basicamente uma combinação de estado e comportamento. Diante disso, devemos dar atenção a como um objeto é usado do quesito concorrência e consistência. Uma maneira de garantir a consistência, seja em ambientes concorrentes ou não, é definindo o objeto como imutável. Ou seja, fazer com que o estado de um objeto seja unicamente definido na sua criação, não podendo ser alterado até o fim do seu ciclo de vida (lifecycle). Como consequência, o comportamento desse objeto será sempre consistente.

Exemplo

Se desenvolvermos usando as boas práticas do padrão Bom Cidadão, iremos ter códigos cada vez menos propensos a erros. Podemos ver um exemplo de um objeto que sempre está consistente do começo ao fim em nossa aplicação abaixo:


// perceba que podemos aplicar o Padrão Bom Cidadão em nossos objetos de domínio também!
public class Usuario {
    private String nome;
    private String email;
    private List telefones;
    public Usuario(String nome, String email) {
        this.nome = nome;
        this.email = email;
    }
    public Usuario(String nome, String email, List telefones) {
        this(nome, email);
        this.telefones = telefones;
    }
    // getNome()
    // getEmail()
    public List getTelefones() {
        // exemplo de imutabilidade; esta lista não pode ser alterada externamente
        return Collections.unmodifiableList(telefones());
    }
    public void add(String telefone) {
        telefones().add(telefone);
    }
    public void remove(String telefone) {
        telefones().remove(telefone);
    }
    private List telefones() {
        if (telefones == null) {
            this.telefones = new ArrayList<>();
        }
        return telefones;
    }
}

Parando para analisar a classe acima, percebemos que não há como alterarmos nosso objeto por fora dele. Toda a regra que permeia pelo usuário só pode ser alterada por ele. Pois inicializamos nosso objeto com todos atributos obrigatórios, eliminando assim os setters do objeto.

Perceba também que podemos inicializar nosso objeto sem os números de telefones, então é necessário verificarmos sempre se a lista de telefones está nula, mas repare que precisamos fazer isso em um único lugar. Não precisamos em lugar nenhum do código externo verificar se o atributo “telefones” do objeto usuario está nulo. Pois qualquer tentativa de obter a lista de telefones do objeto já verifica para nós, ou seja, nunca vamos correr o risco de sofrer um NPE quando trabalhamos com o nosso objeto. Precisamos fazer isso para todas as classes da nossa aplicação, eliminar todos os lugares onde variáveis pode estar nulas e tratar somente dentro da classe. Isso se chama encapsulamento.

Encapsulamento

Você também pode estar me perguntando. E se USUARIO tivesse mais alguns atributos não obrigatórios? E se fosse um objeto e não uma lista? Como retornaria algo que não fosse nulo?

Essa seria uma pergunta muito boa. Muitos dos atributos de nossas classes não são obrigatórios e podem estar nulos. Neste caso, a prática acima não se aplica á esses atributos. Mas parando para refletir, se utilizarmos bem o conceito de encapsulamento e isolar nossas regras que envolve o objeto USUARIO nele mesmo, não corremos o risco de encontrar erros de inconsistência desse objeto pela aplicação, pois estamos garantindo que o objeto está se comportando de maneira adequada e não está sendo modificado.

O encapsulamento que é um assunto muito importante na orientação a objeto, não pode ser resumido apenas em pacotes, modificadores de acesso e simples getters e setters, pois, deixar atributos da classe como privados e fornecer getters e setters não encapsula absolutamente nada. O encapsulamento é isolamento de código nesse caso á modificação do objeto está isolado apenas nele, o que reflete em reuso de código, eliminação de duplicações no código, coesão, entre muitos outros benefícios como fácil manutenção e facilidade de leitura. Devemos abusar do bom encapsulamento.
Há também outras boas práticas que podem virar um novo post que são a utilização do pattern NullObjects e algumas funções do Google Guava, uma delas você pode encontrar aqui.

Conclusão

Há casos que não conseguimos fugir do operador procedural “IF” (sim. isso é procedural 🙁 não fique triste em saber que Java e outras linguagens não são 100% OO), mas novamente, se evitarmos ao máximo que nossos objetos sejam modificados de fora, e encapsularmos bem a nossa lógica, dificilmente vamos precisar verificar a existência de nulo e espalhar if em todo o nosso código, deixando o código com baixa complexidade, alta coesão e fácil de botar a mão.

Não há desculpa para quando recebemos um NPE. E você, ainda recebe NPEs?

Autor

Wilson Souza é Gerente de Marketing da Bluesoft. Formado e pós Graduado pela Instituição Mackenzie, possui também MBA pela FGV. Wilson tem mais de 10 anos de experiência na área de Relacionamento e Marketing, atuando em diversas áreas e segmentos do mercado.

12 Comentários

  1. Leonardo Souza Resposta

    Tenho uma dúvida, como eu faria para encapsular meus atributos corretamente quando uso JSF, por exemplo, em que ele exige que eu tenha gets e sets?

    • Olá Leonardo, tudo bom?
      Muito boa sua pergunta. Nesse caso não há o que fazer, já que o framework exige a existência deles.
      Algumas sugestões minhas são:
      Utilizar o padrão mantendo os setters, mas evitando de usa-los, deixando apenas que o framework utilize-os.
      Desvantagem : É o velho problema: “se está disponível vão usar”
      Uma segunda solução um pouco mais complexa é utilizar um objeto de representação entre a view e o seu objeto de domínio, ex:
      Seu objeto de domínio seguindo as boas práticas

      public class Produto {
      // atributos
      // sem setters
      // objeto consistente
      // com regra de negócio
      }

      Objeto que representará o objeto produto

      public class ProdutoView {
      // atributos
      // com getter e setters
      }

      Quando você precisar trabalhar com o objeto produto vc cria o a partir da sua representação que está populado pelo jsf, trabalhando assim apenas com o objeto consistente pela aplicação.
      Desvantagem: Pode dificultar o desenvolvimento
      Uma última solução é não usá-lo. 🙂
      Desvantagem: Pra mim não tem. Mas gosto é gosto
      Abraço.

      • Lino, muito bom o seu artigo, mas o comentário sobre o JSF tem algumas informações incorretas. O JSF não exige getters e setter e as boas práticas podem ser seguidas normalmente. O que acontece é que o getter e o setter no JSF definem acesso de leitura/escrita ao objeto, sendo assim, se o atributo em questão não tiver setter, a única coisa que acontecerá é que não será possível efetuar alterações ao atributo em questão.
        Além disso, a solução de apresentação criaria uma nova confusão, já que quem usa o objeto de representação não precisa conhecer (e não deve precisar conhecer) o funcionamento do mesmo, e criar setters inócuos poderiam gerar confusões em futuras manutenções.
        Um forte abraço!

        • Opa Missaci. Seria legal se você pudesse nos apresentar essa forma. Trabalhei há muito tempo atrás com JSF e para utilizá-lo, com as suas vantagens, era obrigatório o uso de setters

Deixe aqui o seu comentário