Pesquisar

sexta-feira, 30 de novembro de 2007

Fluent Interface ...

Primeiro a definição depois os exemplos.

*DSL ==> *é uma linguagem (mini liguagem) projetada para um tipo especifico de tarefa, isto é justamente o contrário de linguagens para proposito geral como Java, C ou C#.
*Fluent Interface (Interface Fluente)* ==> objeto que é capaz de deixar as suas responsabilidades mais expressivas a linguagem. (:S confuso, mas o exemplo ajudará)

Para se aprender algo nada melhor do que exemplos./ (aprender com exemplos não dá sono de acordo com Fowler, eu acho que ele está certo)/
Irei apresentar aqui dois exemplos: um simples e outro mais interessante.

*Exemplo Simples*

Supondo que tenho um objeto que representa o conceito Carro.

public class Carro{
private String nome;
private String marca;
private int portas;
public Carro(){
}
// métodos de acesso as propriedades encapsulados...
}

Quando fossemos "usar" esse objeto fariamos...

//***
Carro fusca = new Carro();
fusca.setNome("fusca");
fusca.setMarca("VW");
fusca.setPortas(2);
//***

Tentando aplicar o conceito de Interface Fluente, definiriamos uma Interface

public interface ICarro{
ICarro setNome(String nome);
ICarro setMarca(String nome);
ICarro setPortas(int nome);
}


//classe que implementa essa interface.
public class Carro implements /ICarro/{
private String nome;
private String marca;
private int portas;
public Carro(){
}

public ICarro setNome(String nome){
this.nome = nome;
return this;
}
public ICarro setMarca(String nome){
this.nome = nome;
return this;
}
public ICarro setPortas(int numero){
this.portas = numero;
return this;
}

}

*Agora nosso mesmo exemplo.( O simples ainda )*

//** ANTIGO
Carro fusca = new Carro();
fusca.setNome("fusca");
fusca.setMarca("VW");
fusca.setPortas(2);
//**

//** COM O CONCEITO DE INTERFACE FLUENTE
Carro fusca = new Carro();
fusca.setNome("fusca")
.setMarca("VW")
.setPortas(2);
//Note como cada método de set retorna o objeto aplicando outro método,
dando mais expressividade
//a linguagem.
//**

Isso parece meio bobo, mas para um *exemplo inicial* é até "bonzinho".


*Exemplo Interessante

*Outro possível exemplo, um criador de sql. Em tempos que o padrã JPA está se consolidando, se trata de exemplo não muito usual (sem bem que se pode usar-lo para criar as JPQL)... mas ao exemplo.
Se quiser "conversar" com o SGBD expresse em SQL. Uma consulta SQL é
similar ao exemplo abaixo:*

SELECT* campo1,campo2, campo4 (ou * para todos)
*FROM* tabela1
*[INNER | OUTER ...] JOIN* tabela2 *ON*
*WHERE* condicao1
*AND *condicao2
*OR* condicao2
*ORDER BY* campo1, campo2 ....

O exemplo acima não mostra todas as potencialidades da linguagem SQL mas serve para o propósito do exemplo.

1º A criação da Interface

public interface IConsultaSql{
IConsultaSql select(String campos);
IConsultaSql from(String tabela);
IConsultaSql innerJoin(String tablea);
IConsultaSql outterJoin(String tablea);
IConsultaSql on(String condicao);
IConsultaSql where(String condicao);
IConsultaSql and(String condicao);
IConsultaSql or(String condicao);
IConsultaSql orderBy(String campos);
}

Logo após a criação da classe que implementa a interface.

public class ConsultaSql implements /IConsultaSql/{
private StringBuilder construtor = new StringBuilder();
//note que o próprio StringBuilder aplica conceitos de interface fluente.
public ConsultaSql(){
}

public IConsultaSql select(String campos){
construtor.append("SELECT ")
.append(campos);
return this;
}

public IConsultaSql from(String tabela){
construtor.append(" FROM ")
.append(tabela);
return this;
}

public IConsultaSql innerJoin(String tabela){
construtor.append(" INNER JOIN ")
.append(tabela);
return this;
}
public IConsultaSql outterJoin(String tabela){
construtor.append(" OUTTER JOIN ")
.append(tabela);
return this;
}
public IConsultaSql on(String condicao){
construtor.append(" ON ")
.append(condicao);
return this;
}
public IConsultaSql where(String condicao){
construtor.append(" WHERE ")
.append(condicao);
return this;
}
public IConsultaSql and(String condicao){
construtor.append(" AND ")
.append(condicao);
return this;
}

public IConsultaSql or(String condicao){
construtor.append(" AND ")
.append(condicao);
return this;
}
public IConsultaSql orderBy(String campos){
construtor.append(" ORDER BY ")
.append(condicao);
return this;
}

public String getSql(){
String retorno = construtor.toString();
return retorno;
}
}

*Veja o uso.*

ConsultaSql con = new ConsultaSql();
con.select("nome,telefone")
.from("pessoa_fissica pf").innerJoin("contatos ct").on("pf.id=ct.pf_id")
.where("pf.nome like %?%")
.and("pf.idade>18").orderBy("nome,idade");

*Bem mais expressivo... mais flexível*

Senão engano o Hibernate está implementando [ou tem implementado] algo assim para criar os
objetos Criteria.

public String getSql() throws SqlIncorretoException{
String retorno = construtor.toString();
Sql.valida(retorno);
return retorno;
}


Uma nova característica do java 5, o varargs também pode ajudar a aplicar "esses novos conceitos" de interface fluente.

*TRADICIONALMENTE*
Produto bola;
Produto cafe;
Produto monitor;
Compra compra = new Compra(cliente);
compra.adicionarItem(bola);
compra.adicionarItem(cafe);
compra.adicionarItem(monitor);

*COM USO DE VARARGS
*
Produto bola;
Produto cafe;
Produto monitor;
Compra compra = new Compra(cliente);
compra.adicionarItem(bola,cafe,monitor);

Enfim são todos conceitos muito abstratos e ainda em discussão, logo "nada" é certo sobre esse tema. No mínimo o assunto é interessante e intrigante para os desenvolvedores e clientes (quem realmente interessa). Se acostume com esse novo paradigma /expressividade no desenvolvimento. (tudo mais perto da linguagem de
domínio do sistema)

/ps: perdoem os erros de português.
/Links interessantes sobre os assuntos/.

* _http://www.fragmental.tw_ (Blog sobre pesquisa em DSL)
* _http://martinfowler.com/bliki
/FluentInterface.html_ (Fowler
"ditando" sobre o que é uma Interface Fluente)
* _http://www.infoq.com/presentations/domain-specific-languages_
(Vídeo com uma introdução sobre DSL)

Da arte de integrar sistemas algo é certo... (um pouquinho de S.O.A.)

Integrar sistemas --> Pode ser entendido como a "arte" de interligar diversos sistemas que precisam de SERVIÇOS (não somente dados, além disso é necessário inteligência) dos demais. Essa interligação pode ser feita de diversas formas [implementadas] por exemplo:
Usando WebServices.
Usando RCP. (RMI, COM+ ...)
Usando Tabelas Corporativas.
Usando outros sistemas como "ponte". (sistemas para integração de aplicações)
...

Premissas
É dever do "artista" notar onde há (ou haverá) convergência ou necessidades dos vários sistemas se integrarem. Projetar aplicações já prevendo possíveis integrações é uma ótima e difícil tarefa.

Eis o problema.... (integração via Tabelas Corporativas)
Numa empresa Xyz (empresa de três letrinhas http://blog.fragmental.com.br/2007/06/07/3-letrinhas/) o parque de sistemas conta com aproximadamente 8 grandes sistemas.

Sistema 1 - Recursos Humanos (S1)
Sistema 2 - Capacitação dos Recursos Humanos (S2)
...

O S1 trata do domínio de recursos humanos da empresa, nele há uma tabela corporativa denominada rh_pf para manter informações das várias pessoas fisícas. O S2 no momento de analise percebeu que existiria um conceito de aluno que necessitaria de uma validação (+ que isso um pouco de inteligência) de dados contidos no S1, logo prevendo uma integração futura às aplicações os projetistas tiveram a idéia de usar a mesma tabela [rh_pf] para se resolver este "pequeno problema". (1º tiveram que entender o esquema rh_pf)
Com essa solução houveram outros problemas, quando alguém fosse gerenciar os alunos existentes teria que realizar uma consulta a rh_pf... e agora? voltam todos os registros? {informações sigilosas não podem ser mostradas} E quando fosse alterar, outro sistema iria ter o direito de alterar dados que não pertencem ao contexto do mesmo?!
E as "ligações obrigatórias" {leia-se constraints} contidas em rh_pf como ficariam?

E os projetistas...
Sugeriram outra solução, criar uma coluna na tabela rh_pf chamada sistema, onde poderia se "controlar" quem é dono desses dados, e para as "ligações obrigatórias" resolveram tirar tal integridade (nesse momento o S1 - RH é informado que deverá realizar a verificação da integridade na própria aplicação... mais mudanças...).

Conseqüências...
Dor de cabeça, tempo gasto com outros projetos....
Um ano se passa e outro sistema S3 é obrigado a integrar-se {suas regras de negócio possivelmente necessitam de serviços de outros sistemas}... E a bola de neve cresce...
A computação neste local começou a se tornar centralizada. Se houver um desejo de mudança da tabela rh_pf com certeza vários sistemas irão ter que ser modificados. {Acoplação Extrema entre Sistemas}

Um possível solução
Uma solução seria cada um [dos sistemas] disponibilizar seus serviços... e os demais sistemas aproveitarem esses serviços. Novos sistemas poderiam utilizar os serviços já existentes para desenvolvimento dos mesmos. {conceito próximo a arquitetura S.O.A.}

Conclusão
Algo que eu, particularmente, percebo e que integrar sistemas "via tabelas corporativas" não parece uma boa solução pois os diversos sistemas se tornam muito dependentes uns dos outros, se grandes sistemas [como o dos correios] em uso já descobriram que centralizar não é um bom negócio e que distribuir tem mostrado melhores resultados, pra que centralizar?. [Alias essa é a afirmação da qual deu o título ao post]