Especificação ou implementação?

{ October 27th, 2008 }


cmilfont

Autor: cmilfont

A primeira recomendação em qualquer ramo é sempre seguir a especificação, isso é válido para não cairmos em um dos piores anti-patterns que existe, o “Vendor Lock-in“. Na indústria do Software passamos por isso frequentemente e a estratégia de Embrace-Extend-Extinguish esteve bastante presente na história dos bancos de dados como exemplo disso.

Features proprietárias são sedutoras, antigamente usávamos grids com paginação no HTML com a propriedade datasrc na tag table, podíamos ler inclusive de arquivos XML. Isso muito antes de Ajax ou até do Firefox. Evidente que só funcionava no IE.

<XML ID="users">
<?xml version="1.0" ?>
<users>
	<user id="1" name="Christiano Milfont"/>
	<user id="2" name="Rafael Ponte"/>
</users>
</XML>
 
<TABLE DATASRC="#users">
<TR>
<TD><DIV DATAFLD="id"></DIV></TD>
<TD><DIV DATAFLD="name"></DIV></TD>
</TR>
</TABLE>

Com a popularização do Firefox e a necessidade das aplicações serem CrossBrowser, tivemos que nos adaptar às specs da W3C. Praticamente todas as aplicações WEB precisaram serem reescritas -algumas até hoje ainda só “rodam” no IE.

Usar uma Feature proprietária facilita, mas no final você pagará o preço da não interoperabilidade. Agora precisamos usar um Framework Ajax para fazermos coisas que antes era nativo. No IE tínhamos até DnD.

Especificação muito mais frágil do que as implementações nos força a criar uma “Isolation Layer” como solução de refactoring sem comprometer o sistema. Podemos então usar as features nos beneficiando do que a implementação tem a oferecer sem comprometermos o resultado final, o uso fica encapsulado na solução.

Especificação mal feita

O problema é quando a definição de uma especificação fica frágil o suficiente - por problemas políticos - para forçar um refactoring profundo na mudança entre implementações.

No CEJUG, ocorreu uma thread sobre problema com data na JPA, onde eu recomendei retirar a anotação @Temporal - que já tinha me dado trabalho anteriormente - e por “feeling” eu sabia que o problema era nessa anotação, mas nunca tinha pesquisado para saber o real porquê. Como quem ganhou dinheiro com Feeling no Brasil foi apenas Morris Albert - by Cardoso - eu dei uma pesquisada sobre isso e descobri que [como escrevei no email]:

From CMilfont
to discussao@cejug.dev.java.net
date Mon, Oct 27, 2008 at 9:54 AM
subject Re: [cejug-discussao] problema com data

Dei uma pesquisada e a conclusão que cheguei é:
A spec determina que propriedades do tipo Date e Calendar [util java] devem ter a anotação @Temporal.
TopLink obriga, se não tiver lança uma exceção ValidationException [pelo menos foi o que vi na documentação dele, não cheguei a testar].
Hibernate é opcional, mas se você colocar ele devolve uma instância de Calendar, porque entende que a data é completa [como Timestamp] mesmo dizendo que o tipo é Date - aqui entendo como uma falha e vi que as issues sobre isso no projeto já foram fechadas, os últimos builds devem ter consertado, ou não.
A spec não diz que deve lançar exception mesmo dizendo “must be” então o Oracle TopLink assumiu essa responsabilidade.
Claro que essas funcionalidades devem também mudar de build para build então podem ter diferenças nas versões de builds entre os próprios implementadores, como Hibernate e TopLink mudarem da versão x.x.1 para x.x.3 por exemplo.
Coisas de spec mal escrita, JPA tem que ser urgente revista, os capítulos ficam muito ambiguos, tem trechos que você fica bastante confuso, diferente de specs como da JSE que é bastante clara.

O problema disso é que não dá para encapsular a diferença entre as implementações porque o uso dessa anotação é incompatível entre dois desses implementadores.

Outro problema da JPA é a falta de elementos básicos que um ORM deve implementar - como a Criteria - impossibilitando a troca de implementações com um simples refactoring. Tudo bem que em um Domain Model você tem como - e deve - isolar essa diferença entre Engines ORM mas em termos de Refactoring da aplicação no geral, ter que reescrever todas as buscas de uma aplicação porque a Engine não suporta Criteria pode não afetar o modelo mais trará um prejuízo enorme.

Como o Hibernate é OpenSource, maduro e anos-luz à frente até da nova especificação de JPA 2.0, a escolha dele pelos profissionais experientes é a mais adequada. Spring, é outra solução ao JEE sem EJB que permanece como indicação desde o livro do Rod Johson de 2004 que era ainda sobre EJB2, recomendava o uso de JEE sem EJB e apresentou o Spring oficialmente ao mundo. Apesar de que Spring e EJB3 estarem bem nivelados hoje em dia, Spring ainda leva uma certa vantagem, ainda mais pela maturidade e semrpe estar nos trilhos corretos, coisa que o EJB está se ajustando aos trancos e barrancos.

Em regra eu sempre recomendo o uso de especificações, mas em determinados pontos a especificação é desaconselhada e o uso direto da implementação tem suporte mais adequado. Não temos o poder de sempre escolhermos as ferramentas mais adequadas, muitas vezes a política impera - como nos órgãos públicos que são obrigados a usarem Oracle ou IBM por imposição governamental feita em um escritório na Casa Civil.

Posted in Engenharia de Software, JEE, Java, Linguagens, Melhores práticas, Orientação a Objetos, Software Livre ~ 5 Comments

Adicionar ao Rec6

Java Magazine ed 61

{ September 30th, 2008 }


cmilfont

Autor: cmilfont


Saiu a edição 61 da Java Magazine com matéria minha publicada sobre Orientação a Objetos no Javascript. TEm artigo de um conterrâneo e da turma de Natal também.

Agora são 3 artigos meus publicados http://www.milfont.org/tech/published/

http://www.devmedia.com.br/resumo/default.asp?site=6

Escritores de Fortaleza

Orientação a Objetos no Javascript
Por: Christiano Milfont

Nesse artigo estudaremos como está fundamentada a orientação a objetos no Javascript e como entender a sintaxe dentro desse conceito. Todos os Frameworks Ajax modernos estão codificados com a orientação a objetos como base.

MP3 Player ME
Por: Ernandes Mourão Júnior

Neste artigo, vamos mostrar como podemos inserir a plataforma Java ME nesta estória. Vamos criar um cenário onde o seu dispositivo móvel não possui um tocador de MP3, mas você gostaria de ter um para poder ouvir as suas canções favoritas. Para isto, vamos mostrar o desenvolvimento de um tocador de MP3 simples, utilizando-se da JSR 75 – PDA Optional Packages e da JSR 135 – Mobile Media API (MMAPI). Explorando-as desde os conceitos até a prática, com exemplos de código.

Escritores de Natal

Gráficos em Sistemas Web
Por: David Pereira, Itamir Filho e Raphaela Galhardo

O artigo apresenta um conjunto de técnicas para criação de gráficos em sistemas web.

Posted in Java Magazine, JavaScript, Linguagens, Orientação a Objetos ~ 2 Comments

Adicionar ao Rec6

Regras dinâmicas no Domain Model com linguagens de script

{ January 22nd, 2008 }


cmilfont

Autor: cmilfont

Trabalhar com Orientação a Objetos em aplicações comerciais é trabalhoso porque envolve necessariamente outros paradigmas (como bancos de dados relacionais), além de requisitos não funcionais da sua arquitetura (como segurança, concorrência, etc) impedindo uma modelagem pura. Em aplicações que realmente existem lógica de negócios e não apenas um conjunto de cadastros e relatórios, a utilização de uma abordagem com “Domain Driven Design” se faz necessária para a redução da complexidade no coração do software, ou seja, na camada responsável pelo modelo do negócio.

A abordagem de Domain Model proposta por Eric Evans, consegue isolar o núcleo do sistema (responsável pela lógica de manipulação das informações e processos de negócios) abstraindo das particularidades da arquitetura por meio de suas técnicas modernas e maduras. Mesmo assim o esforço de isolamento não é tão claro mesmo para coisas triviais como segurança, onde existe uma linha tênue entre o que é requisito funcional ou não.

Na construção do seu modelo, a profusão de abordagens como Fluent Interfaces e DSL ajudam a maximizar o entendimento do negócio, mas ainda assim restam dúvidas de como e onde aplicar adequadamente.

Confusão

Tenho visto muitos bons desenvolvedores caírem na onda do Hype e se tornarem fanboys “*-tards” e saírem por aí alegando que a linguagem X ou outra coisa qualquer é evolução do Java/C# ou coisas desse tipo, comparando linguagens de abordagens tão distintas entre si e até de paradigmas diferentes. Esse pessoal esquece “o porquê” de existirem tipos estáticos e dinâmicos, fortes e fracos. Uma linguagem funcional não é evolução de uma linguagem orientada a objetos, se bem que o vice-versa é contraditório porque alguns autores alegam que o object calculi seria evolução do lambda-calculi. Linguagens de tipos dinâmicos não são evolução de tipos estáticos, já existiam e servem para propósitos diferentes. Da mesma forma existem JAVAtards que esquecem que existem outras funcionalidades que Java não implementa (lembre-se que você não cria um MapReduce sem conhecimentos profundos de programação funcional).

O ponto principal é que os tipos estáticos são importantes em linguagens imperativas e são importantes na definição da camada de negócio, mas linguagens dinâmicas também o são, pela agilidade nas mudanças e na complexidade da arquitetura. Imagine o custo de um deploy para cada refactoring e mudança em uma arquitetura JEE e compare com o mesmo nível de complexidade de mudança em PHP ou Ruby.

O mais comum ultimamente é a divisão da camada de negócios em duas zonas distintas: uma estática, implementada com uma linguagem estática e fortemente tipada como Java; e uma dinâmica, implementada com o sabor de script que você goste, como ruby, javascript ou python.

Camada estática

Existem princípios em qualquer negócio que por mais ágil que esse negócio seja e mude constantemente, esses princípios nunca mudam, são aquelas funcionalidades facilmente detectadas na modelagem tradicional, onde se detecta o caminho principal de um ator pelo caso de uso e podemos prever os caminhos alternativos.

Alguns pseudoanalistas imaginam que todas as funcionalidades se revelam na modelagem tradicional e se não cobriu todas as possibilidades é porque falhou o processo de desenvolvimento. Essas funcionalidades facilmente detectáveis e controláveis fazem parte da camada estática do seu modelo, não devem mudar nunca ou muito pouco em relação à vida útil do software.

Imagine um sistema contábil, os princípios de crédito e débito, plano de contas, livros contábeis, a própria operação de contabilização a partir de documentos entre outros aspectos são os mesmos há uns 200 anos e mudaram muito pouco nesse tempo todo. Falo sobretudo da contabilidade fiscal. Uma modelagem desses princípios vai sofrer pouca alteração durante toda a vida útil do software.

Imagine o sistema jurídico, é praticamente o mesmo dos romanos. Imagine um sistema de vendas, desde a troca de sal por um carneiro, surgimento da moeda ao cartão de crédito, os princípios são os mesmos.

Para implementar essa camada, uma linguagem de tipos fortes e estáticos é o mais indicado.

Camada dinâmica

Agora imagine toda a contabilidade gerencial que é realizada com base na contabilidade fiscal. Imagine todas as operações possíveis e combinações imaginárias de vendas. Todos os cálculos de impostos que os governos mudam todo ano. A análise e modelagem total dos casos de usos é praticamente impossível e se mostrou inviável ao longo do tempo.

Alguém pode sugerir a utilização de métodos ágeis para solucionar esse problema em linguagens estáticas mas não é o bastante. Os métodos ágeis surgiram para prepararem as equipes para as mudanças, suavizar o impacto que refactoring causam no código. Observem que estamos falando apenas de negócio, puro negócio. Os métodos ágeis não dizem como fazer o seu código, eles te auxiliam.

A abordagem de utilizar uma linguagem dinâmica para esses requisitos mutáveis e tardiamente diagnosticáveis se mostrou eficientíssima. Isolamos parte do Domain Model para ser provido de uma linguagem dinâmica (de script de preferência), e concentramos na facilidade de montar um idioma comum para a equipe sem o custo de build ou deploy de linguagens estáticas.

Segmentação

Dividimos então essa camada dinâmica em duas camadas interiores: uma da própria linguagem dinâmica que facilita as modificações ágeis (aconselhavelmente com Fluent Interfaces); e outra mais rebuscada, criada para a criação de regras (DSL com ou sem Fluent Interfaces) e uma linguagem comum entre a equipe e os operadores do sistema que são geralmente não técnicos mas treinados para isso.

Implementação

Trabalhemos com esse pequeno exemplo, tenho uma operação fecharVenda de uma hipotetica classe Venda que entre suas responsabilidades, existe a geração de comissão do vendedor.

1
2
3
4
5
6
7
public void fecharVenda() {
 ...
	//pre-execução
 	this.vendedor.setComissao(this.valorTotal * (5/100) );
	//pos-execução
...
}

Agora imagine que surgiu uma modificação no sistema, existem dois percentuais diferentes. Isso é moleza, você refatora a operação com a modificação:

1
2
3
4
5
6
7
8
9
10
11
12
13
public void fecharVenda() {
 ...
 
	//pre-execução
	if(this.tipo.equals("VendaTipo1"))
		this.vendedor.setComissao(this.valorTotal * (5/100) );
	}
	if(this.tipo.equals("VendaTipo2"))
		this.vendedor.setComissao(this.valorTotal * (8/100) );
	}
	//pos-execução
...
}

Ah! Mas eu sou esperto, criei uma propriedade lá que amarro o tipo de documento com o percentual apropriado:

1
2
3
4
5
6
7
8
public void fecharVenda() {
 ...
	//pre-execução
	this.vendedor.setComissao(this.valorTotal 
                   * this.vendedor.getPercentualApropriado() );
	//pos-execução
...
}

Daí chega o seu cliente todo fogoso e diz que a regra mudou, agora não é mais no total da nota e sim nos ítens, cada item pode ter um percentual diferente. O caminho mais usual é você refatorar o item e copiar a propriedade para lá, percorrer todos os produtos no método fecharVenda e calcular a comissão, claro, deixando a abordagem atual comentada caso o cliente deseje voltar como era antes.

Usando uma linguagem de Script

Observe no comentário //pre e //pos execução, que acha de injetar uma chamada a um script no método?

Você faria essa última modificação assim em javascript (com Rhino):

Chamada ao Script que será injetado no modelo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("JavaScript");
try {
	engine.put("lista", this.itens);
	engine.put("vendedor", this.vendedor);
	engine.put("nf", this);
	engine.put("total", this.total);
	engine.put("totalDescontos", this.totalDescontos);
	String path = NotaFiscal.class.getResource("script.js").getPath();
	engine.eval(new FileReader(path));
	this.total = (Double) engine.get("total");
	this.totalDescontos = (Double) engine.get("totalDescontos");
} catch (ScriptException e) {
	e.printStackTrace();
} catch (FileNotFoundException e) {
	e.printStackTrace();
}

Código no script.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
(function() {
	var _$ = function(els) {
		this.elements = [];
		var iterator = els.iterator();
		while(iterator.hasNext()) {
			this.elements.push(iterator.next());
		}
		return this;
	};
	_$.prototype = {
		each: function(fn) {
			for ( var i = 0, len = this.elements.length; i &lt; len; ++i ) {
				fn.call(this, this.elements[i]);
			};
			return this;
		},
		comissionar:function(vendedor) {
			var comissao = function(e) {
				vendedor.comissao += (e.total * e.percentual);
			}
			this.each(comissao);
		}
	};
	$ = function() {
		return new _$(arguments[0]);
	};
}());
$('lista').comissionar();

Fortemente inspirado no código do prototype resolvi o problema temporariamente sem precisar refatorar o código da aplicação e deixei aberto para quantas modificações forem necessárias nessa parte que é variável.

Posted in DSL, Engenharia de Software, Fluent Interface, Java, Linguagens, Melhores práticas, Orientação a Objetos ~ 16 Comments

Adicionar ao Rec6