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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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!