segunda-feira, 6 de novembro de 2017

Programação defensiva com Decorator: simplificando a verificação de nulos.

Olá, pessoal!

Muitas vezes nos deparamos sempre com os mesmos problemas simples e não paramos para pensar em como resolvê-los de forma mais eficiente. Já dei um exemplo assim aqui no blog de como um problema simples pode ter inúmeras soluções diferentes.

Agora, que tal aprender a fazer um:

if (objeto == null) {
throw new IllegalArgumentException();
}

Apenas instanciando a mesma classe?

Problema


Vamos começar então. Imaginamos que tenhamos uma classe de nome Pessoa e que ela tenha um parâmetro nome:

class Pessoa {
private String nome;
Pessoa (String nome) {
this.nome = nome;
}
}


E agora você deseja que o nome não seja nulo. Usualmente, a dúvida de muitos desenvolvedores é se o if será colocado no construtor, no set ou ainda em alguma classe de serviço responsável por criar pessoas.

Então, não é comum terminarmos com uma solução assim:

class Pessoa {
private String nome;
Pessoa (String nome) {
if (nome == null) {
throw new IllegalArgumentException();
}
this.nome = nome;
}
}


Se depois aparecer um objeto Endereco, será a mesma coisa. Se aparecer um objeto Rua, será a mesma coisa. Ainda que apareça um parâmetro novo para Pessoa, como um sobrenome, teremos o mesmo caso. Logo verá que o código ficará bem repetitivo:

class Pessoa {
private String nome;
private String sobrenome;
private Cidade cidade;
Pessoa (String nome, String sobrenome, Cidade cidade) {
if (nome == null) {
throw new IllegalArgumentException();
}
if (sobrenome == null) {
throw new IllegalArgumentException();
}
if (cidade == null) {
throw new IllegalArgumentException();
}
this.nome = nome;
this.sobrenome = sobrenome;
this.cidade = cidade;
}
}
view raw pessoa-v3.java hosted with ❤ by GitHub


Sendo que tudo que queremos, afinal, é garantir que nome, sobrenome e cidade não sejam nulos.

Alternativa


Como alternativa, se não queremos objetos nulos, podemos criar um objeto NotNull, desta maneira:

public class NotNull<t> {
private T verified;
private NotNull() {
}
public NotNull(final T verify) {
if (verify == null) {
throw new IllegalArgumentException("must not be null");
}
this.verified = verify;
}
public final T getVerified() {
return this.verified;
}
}
view raw notnull.java hosted with ❤ by GitHub


Esta classe usa generics, permitindo aceitar qualquer tipo de objeto como parâmetro e fazendo a esperada condição de verificar se o objeto é nulo.

Desta maneira, podemos simplificar nossa classe Pessoa, fazendo com que ela receba nome, sobrenome e cidade não nulos, determinando isto já no contrato da classe Pessoa:

class Pessoa {
private String nome;
private String sobrenome;
private Cidade cidade;
Pessoa (NotNull<String> nome, NotNull<String> sobrenome, NotNull<Cidade> cidade) {
this.nome = nome.getVerified();
this.sobrenome = sobrenome.getVerified();
this.cidade = cidade.getVerified();
}
}


E para instanciar uma classe Pessoa, basta:

Cidade londrina = new Cidade(new NotNull<String>("Londrina")); // mesma ideia do objeto Pessoa aplicado na Cidade
Pessoa pessoa = new Pessoa (new NotNull<String>("Roberto"), new NotNull<String>("Oliveira"), new NotNull<Cidade>(londrina))


Podemos também criar outras classes de verificação: NotEmpty, NotNegative (para números), ValidDate (para parse de datas), etc, e podemos combinar várias delas umas com as outras. Por exemplo, você pode querer que nome não seja nulo, não seja vazio e não contenha caracteres especiais, resultando em uma chamada parecida com esta:

Pessoa pessoa = new Pessoa (
new NoSpecialChar<String>(
new NotEmpty<String>(
new NotNull<String>("Roberto")))
);


Usando assim o poder do Decorator :). Trarei mais exemplos desta natureza em um futuro texto.

Dúvidas ou sugestões, usem os comentários. Obrigado!