quinta-feira, 14 de outubro de 2010

Habilitando dominios ricos no SEAM

quinta-feira, 14 de outubro de 2010
A metodologia DDD enfatiza que ao desenvolvermos as regras de negócios devem ficar na camada de domínio, concentrando todo os relacionamentos e o funcionamento do sistema nesta camada, portanto poderíamos ter um método como:

cliente.pagar(faturaEmAtrazo);
Cliente é uma entidade do sistema, com isso não quero dizer que o cliente deve ter a lógica de pagamento de faturas, mais é ele quem inicia o processo, de onde então vem a lógica? De uma service:
public class ControleDeFaturas {
  public void receberPagamento(Cliente cliente,Fatura fatura){
    .... // aqui residiria a lógica de 
  }
}
e o cliente como fica? Ele recebe por Dependency Injection o serviço de controle de faturas, usando o framework seam teriamos algo como:
@Entity
public class Cliente {

  @In
  private ControleDeFaturas controleDeFaturas;

  ...

  public void pagar(Fatura fatura){
    controleDeFaturas.receberPagamento(this,fatura);
  }

}
Assim o modelo fica melhor, agora um problema que temos no SEAM é que ele não faz injeção de dependências nas entitys, para resolver este problema temos várias soluções, poderíamos usar a entity com uma factory que receberia o controleDeFaturas, ou mesmo fazer lookup da dependência usando Component.getInstance("controleDeFaturas"), uma abordagem que usei a qual estou gostando é o uso de Aspectos, aspecto resolve este problema, de uma forma transparente, onde mesmo se instanciarmos o Cliente por meio de um new Cliente(); ainda teríamos a injeção da dependência.

você pode visualizar a classe aqui que resolveu meu problema (usa aspectj).


Com certeza a classe pode ser melhorada, mas inicialmente resolveu o problema e permitiu uma habilitação de um domínio rico usando o SEAM, basicamente ele injeta uma instancia sempre que encontra uma classe com a anotação @Entity e um @In tanto faz ser nos atributos da classe, ou nos métodos acessores.

Devo agradecer ao Alessandro Lazarotti que sugeriu o aspecto acima como solução no fórum do guj.


Nenhum comentário :

quarta-feira, 6 de outubro de 2010

Fluent Interfaces e operadores Lógicos

quarta-feira, 6 de outubro de 2010
Algo com que todos os desenvolvedores tiveram como problema é a multiplicade de operações repetidas, creio que um exemplo seja melhor.

Reparem neste trecho de código:
public class LocalizadorDeClientes{
    public List<cliente> buscarPorCPF(String cpf){
        //implementação da busca.
    }
    
    public List<cliente> buscarPorNome(String nome){
        // implementação da busca.
    }
}
O cliente solicita agora que a busca seja feita por cliente inadimplente, a implementação mais comum seria mais um método de pesquisa:
public List<cliente> buscarInadiplentes(){
...
}
Essa abordagem é ruim? Não necessariamente, mas podemos usar um esquema diferente, o cliente te solicita agora quero combinar os filtros, muitos diriam que não dá ou fariam métodos diversos para todas as combinações possíveis.

Para casos como estes em que queremos combinar comportamentos poderemos tirar proveito da fluent interfaces:

Os exemplos apresentados aqui foram feitos apenas para exemplificar a metodologia podendo variar em implementação.

Reparem no exemplo modificado, usando fluent interfaces:
public class LocalizadorDeClientes{
public LocalizadorDeClientes porCPF(String cpf){
//configura a busca para ser realizada por cpf
return this;
}

public LocalizadorDeClientes porNome(String nome){
//configura a busca para ser realizada por nome
return this;
}

public LocalizadorDeClientes inadimplente(){
//configura a busca para ser realizada por inadimplentes
return this;
}

public List<cliente> fazerBusca(){
//configura a busca para ser realizada por inadimplentes
return this;
}

}
o uso seria assim:
LocalizadorDeClientes buscador = new LocalizadorDeClientes();
buscador.PorCPF(numCPF).porNome("carlos").inadimplente().fazerBusca();
Reparem que agora você pode encadear a chamada, fazendo com que a busca por exemplo possa ser configurável.

Este exemplo pode ser estendido usando um esquema de operadores lógicos, bastaria adicionar os métodos na classe:
public LocalizadorDeClientes e(){
// configura a busca para executar com o operador e
// no hibernate seria um conjunction em memória seria um &&
return this;
}

public LocalizadorDeClientes ou(){
// configura a busca para executar com o operador ou
// no hibernate seria um disjunction em memória seria um ||
return this;
}

public LocalizadorDeClientes nao(){
// configura a busca para executar com o operador nao
// no hibernate seria um Restriction.not em memória seria um !
return this;
}
Agora poderia fazer as consultas por exemplo assim:
LocalizadorDeClientes buscador = new LocalizadorDeClientes();
buscador.PorCPF(numCPF).e().porNome("carlos").nao().inadimplente().fazerBusca();
um esquema como este de fluent interfaces permite compor comportamento complexo a partir de um conjunto de comportamentos simples, a projetos que já utilizam isso como o Calopsita isso foi até mesmo discutido recentemente o Tectura no tópico nomes Complexos/descritivos no modelo e Martin flower fala exatamente sobre isso em seu artigo sobre fluent interfaces.

Tente entender o que é fluent interfaces, e o seu uso com operadores lógicos, pois ajudam a ter um domínio mais rico em seu software.


Nenhum comentário :

quinta-feira, 19 de agosto de 2010

Repita comigo cada teste no seu galho

quinta-feira, 19 de agosto de 2010
Algo que pode dificultar um pouco para quem está iniciando a programar usando TDD ou BDD e conseguir fazer a distinção do que é um teste de unidade de um teste funcional.

O problema que geralmente se encontra é o que o teste funcional pode parecer muito com o seu teste de unidade, mas sem estar mockado.

Isso é normal e não deve ser encarado como uma duplicação do código de teste.

Basta lembrar do objeto dos testes, um teste Funcional responde se determinada funcionalidade esta ok, portanto não usamos mocks mas objetos reais, o que desejamos é validar a interação entre os objetos, esse tipo de teste te responde "você está com problema aqui nessa funcionalidade", o que difere do teste de unidade pois o objetivo dele é validar a lógica interna de sua classe, portanto a resposta de uma falha em um teste de unidade é "você esta com problema na linha x".


Um comentário :

quinta-feira, 12 de agosto de 2010

TDD e o design de sua aplicação

quinta-feira, 12 de agosto de 2010
Ao programarmos é muito comum não termos um escopo muito bem definido do deve ser desenvolvido, embora saibamos como devemos criar os nossos códigos.

Em que TDD ajuda no design da sua aplicação? Simples, ele o mantém limpo a medida que descobre novas funcionalidades.

Suponhamos que estamos usando o estilo de fora para dentro na construção de nossos testes, devemos então começar pela interface do usuário, portanto teríamos o seguinte teste:

public class CadastroClienteTest {
  @Test public void deveriaEmitirMensagemDeAvisoParaClienteEmAtrazoAoSelecionarCliente(){}
}

Esse teste serve de ponto de partida para o seu design lembre-se que aqui você ainda não pensou em acesso ao banco e em nada de services etc..., apenas disse uma funcionalidade que a sua interface deveria ter, ao ir implementando os métodos e usando uma classe para mockar as suas dependências, você vai desenhando o seu código, implementando apenas aquilo que deve ser implementado.

Teriamos o seguinte teste:
public class CadastroClienteTest {
  
  private CadastroClienteManageBean telaCadastro;  
  private FacesMessages facesMessages;

  @Before public void configuraDependencias(){
    facesMessages = Mockito.mock(FacesMessages.class);
    telaCadastro = new CadastroClienteManageBean();
    telaCadastro.setFacesMessages(facesMessages);
  }

  @Test public void deveriaEmitirMensagemDeAvisoParaClienteEmAtrazoAoSelecionarCliente(){
     telaCadastro.selecionarCliente(1);
     Mockito.verify(facesMessages).add(FacesMessage.SEVERITY_ERROR,"cliente em atrazo");
  }
}
e sua respectiva classe:

public class CadastroClienteManageBean {

  private FacesMessages facesMessages;

  public void selecionarCliente(Integer id){
    // deveria ir ao banco
    facesMessages.add(FacesMessage.SEVERITY_ERROR,"cliente em atrazo");
  }
  
  public void setFacesMessages(FacesMessages facesMessages){
    this.facesMessages = facesMessages;
  }

}

Agora reparando neste código perceba que o sistema não está indo até o banco, para de fato trazer o resultado, ai entra algo que acho incrível do TDD, ele te ajuda a ter um design limpo.

vamos agora melhorar o teste, para fins de simplificação não iremos criar um service para o acesso ao banco, vamos conectar diretamente:

public class CadastroClienteTest {
  
  private CadastroClienteManageBean telaCadastro;  
  private FacesMessages facesMessages;

  @Before public void configuraDependencias(){
    facesMessages = Mockito.mock(FacesMessages.class);
    registrosDeClientes = Mockito.mock(RegistrosDeClientes.class);
    telaCadastro = new CadastroClienteManageBean();
    telaCadastro.setFacesMessages(facesMessages);

  }

  @Test public void deveriaEmitirMensagemDeAvisoParaClienteEmAtrazoAoSelecionarCliente(){
     Cliente clienteEmAtrazo = new Cliente();
     clienteEmAtrazo.setEmAtrazo(true);
     Mockito.when(registrosDeClientes.buscarCliente(1)).thenReturn(clienteEmAtrazo);
     telaCadastro.selecionarCliente(1);
     Mockito.verify(registrosDeClientes).buscarCliente(1);
     Mockito.verify(facesMessages).add(FacesMessage.SEVERITY_ERROR,"cliente em atrazo");
  }
}

public interface RegistrosDeClientes {

  public Cliente buscarCliente(Integer id);

}

Repare que o seu teste agora está sugerindo que você tenha um método de busca, uma vez que você tenha terminado o seu teste na tela, uma abordagem seria descer o nível e pegar todas as classes que você mockou no teste da interface e agora ir implementando o teste para as camadas mais baixas.

TDD ajuda a melhorar o design, fazendo você programar aquilo que você precisa implementar, ao invés de ficar criando métodos para classes que você acha que irá precisar.

O método usado aqui baseasse no teste de fora para dentro, onde fomos construindo um teste para a interface e mockando os objetos das camadas abaixo, e posteriormente deveríamos implementar os testes das camadas mais baixas, com os dados que coletamos dos mocks da camada superior.

Em resumo o mock te sugere o que pode ser necessário que você tenha que implementar, caso não tenha usado ainda TDD, faça um teste e se esforce, pois realmente vale a pena.


Nenhum comentário :

quinta-feira, 14 de janeiro de 2010

Framework JBee Behavior-driven development Lançado

quinta-feira, 14 de janeiro de 2010
É bem aceito na comunidade um novo paradigma de desenvolvimento de sistemas o TDD, em que consiste o desenvolvimento de testes antes da aplicação ser construida, utimamente em ganhado muita força o BDD que é o desenvolvimento orientado a comportamento, os benefícios de aplicar o BDD é a facilidade de fazer testes de aceitação e regressão no sistema.

Para programar usando a metodologia do BDD (Behavior-driven development) o desenvolvedor escreve uma história que consiste nos passos para o usuário, sendo que esta história é coduzida pelo uso de palavras chaves (Dado que, Quando, Então, E) que são interpretadas e executadas no sistema.

Para facilitar o desenvolvimento orientado a BDD eu desenvolvi um framework chamado JBee onde o objetivo dele é facilitar o uso desta metodologia de testes.

O framework é compatível com o Junit.

Acessem a pagina http://sites.google.com/site/jbeetest/ para conhecer o framework e ver a sua utilidade em ação.


Nenhum comentário :