Sistemas complexos dispões de muitas regras de negócio que muitas vezes precisam ser reutilizadas em diferentes módulos, no entanto, não é uma boa prática ficar repetindo código em diferentes lugares de seu código fonte.
Pensando nisso, criei um framework que facilita a criação e o reaproveitamento de regras de négocio, os fontes do mesmo encontram-se disponíveis no meu repositório do GitHub e a documentação está disponível nos meus JavaDocs.
A interface pública do framework é composta pelos seguintes tipos:
-
Interface Specification, que deve ser implementada pela sua classe que possui a implementação da regra de negócio;
-
Classe de exceção UnattendedException, que deve ser lançada quando um objeto não atende à determinada regra;
-
Classe Rule, responsável por transformar a implementação da Specification em uma regra reaproveitável.
Para entender como o framework funciona na prática, veja o exemplo abaixo:
Imagine que uma pessoa possui os seguintes atributos: nome, idade e sexo. Para validar uma pessoa, foram definidas as seguintes regras:
-
O nome deve começar com uma letra maiúscula e ter uma ou mais letras minúsculas;
-
A idade não pode ser negativa;
-
O sexo só pode ser 'M' ou 'F'.
Primeiro definimos a classe Pessoa conforme abaixo:
class Pessoa {
String nome;
int idade;
char sexo;
Pessoa(String nome, int idade, char sexo) {
this.nome = nome;
this.idade = idade;
this.sexo = sexo;
}
}
Agora criamos três especificações distintas para as regras que validam nome, idade e sexo da Pessoa.
Especificação da validação do nome:
import br.com.staroski.rules.*;
// Especificação da regra que valida o nome de uma Pessoa
class Nome implements Specification {
public void verify(Pessoa pessoa) throws UnattendedException {
if (!pessoa.nome.matches("[A-Z]{1}[a-z]+")) {
throw new UnattendedException("Nome precisa começar com letra maiúscula e ter pelo menos duas letras");
}
}
}
Especificação da validação da idade:
import br.com.staroski.rules.*;
// Especificação da regra que valida a idade de Pessoa
class Idade implements Specification {
public void verify(Pessoa pessoa) throws UnattendedException {
if (pessoa.idade < 0) {
throw new UnattendedException("Idade não pode ser negativa");
}
}
}
Especificação da validação do sexo:
import br.com.staroski.rules.*;
// Especificação da regra que valida o sexo de uma Pessoa
class Sexo implements Specification {
public void verify(Pessoa pessoa) throws UnattendedException {
switch (pessoa.sexo) {
case 'M':
case 'F':
return;
default:
throw new UnattendedException("Sexo só pode ser 'M' ou 'F'");
}
}
}
Agora ja temos a classe Pessoa e as especificações das regras para nome, idade e sexo criadas. Podemos então utilizar a classe Rule para validar instancias de Pessoa de diversas formas, por exemplo:
import br.com.staroski.rules.*;
public class Exemplo {
public static void main(String[] args) {
// instanciamos as regras a partir das especificações
Rule nome = Rule.create(new Nome());
Rule idade = Rule.create(new Idade());
Rule sexo = Rule.create(new Sexo());
// criamos uma pessoa com nome, idade e sexo validos
Pessoa pessoa = new Pessoa("Fulano", 30, 'M');
// criamos uma regra só que corresponde às três regras: nome, idade e sexo
// e validamos com um único if
if (nome.and(idade).and(sexo).isSatisfiedBy(pessoa)) {
System.out.println("Teste 1");
System.out.println("O nome, idade e sexo da pessoa atendem as regras\n");
}
// criamos uma pessoa com nome, idade e sexo inválidos
pessoa = new Pessoa("FuLaNo", -1, 'S');
// criamos uma regra só que corresponde às três regras: nome, idade e sexo
// armazenamos essa regra numa variável
Rule regra = nome.and(idade).and(sexo);
// assim, validamos as três regras, com um único if
if (regra.not().isSatisfiedBy(pessoa)) {
System.out.println("Teste 2");
System.out.println("A pessoa não atendeu às seguintes regras:");
// se a pessoa não atendeu às regras,
// usamos a variável declarada para obter os detalhes
for (String detalhe : regra.getDetails()) {
System.out.println(detalhe);
}
}
}
}
Executando a classe Exemplo acima, obteremos a seguinte saída:
Teste 1
O nome, idade e sexo da pessoa atendem as regras
Teste 2
A pessoa não atendeu as seguintes regras:
Nome precisa começar com letra maiúscula e ter pelo menos duas letras
Idade não pode ser negativa
Sexo só pode ser 'M' ou 'F'
Assim podemos observar como o uso de regras reutilizáveis podem facilitar a implementação e manutenção de validações nos mais variados sistemas.