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 < 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

Não use notação estranha

{ January 21st, 2008 }


cmilfont

Autor: cmilfont

A notação húngara teve sua época e sua utilidade, basicamente serve para diferenciar tipos. Hoje com o avanço das IDEs nem linguagens de tipos fracos precisam de notação húngara.

A notação húngara influenciou outras áreas como Banco de dados. Criaram notações para objetos de banco devido às deficiências das ferramentas em identificar tipos, os DBAs mantiveram o costume de padronizar os nomes dos artefatos com prefixos e as vezes tambem com sufixos. Na SEAD-Ce usávamos “TB_” como prefixo de tabelas para diferenciar de “TR_” para triggers. Isso tem uma valia grande para um DBA na hora de listar os objetos no Oracle ou criar rotinas de manipulação.

Mas pelo amor de Javé, não use isso em uma linguagem orientada a objetos, ainda mais com tipos fortes e estáticos como JAVA.

Imagine a seguinte situação em java:

Class Categoria {
 
    private String nomeCategoria;
 
    //segue ...
 
}

Se nome é uma propriedade da classe Categoria, para que diabos nomear como nomeCategoria? Com qual Objetivo? Qual a vantagem que isso trás?

O pior é definirem padrões semelhantes ao que o DBA (que tem necessidade disso) define para seus artefatos em uma linguagem OO. Vi padrão definido como “usar as três primeiras letras da classe antes das propriedades” e todo tipo de monstruosidade.

Nem que voce me prove que usa o bloco de notas para programar, eu ficaria convencido da real utilidade disso.

Dar manutenção em código desse tipo mais atrapalha do que ajuda, fora que construir também nunca vi utilidade nisso.

Usar notação Java padronizada pela SUN tudo bem , como definir o nome de variáveis em minúsculo e as demais palavras com a primeira letra em maiúsculo, mesmo assim é uma sugestão para facilitar o reconhecimento pela comunidade, se você não quiser seguir o código compila numa boa. Outras comunidades definem a segunda palavra separada por “_” como data_inicio. Mas nada de PTcaixaDoisDTO por favor.

Não invente notação estranha para seu código, imagine que a pessoa que vai dar manutenção é o Dexter Morgan e ele sabe seu endereço.

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

Adicionar ao Rec6

Herança no Javascript

{ January 5th, 2008 }


cmilfont

Autor: cmilfont

Herança é capacidade de um objeto reusar (herdar) os métodos e propriedades de outro objeto. O modelo de orientação a objetos baseado em protótipos, estabelece que um objeto serve de “molde” ou “base” (protótipo) para outro e a linguagem javascript adiciona a propriedade global “instanceof” para definir se um objeto “é” do mesmo tipo do objeto base (protótipo). Para implementar a herança, voce define um objeto base (protótipo) que fornecerá as características aos outros objetos.

Todos os objetos definidos pelo desenvolvedor são candidatos a objeto base. Como medida de segurança as classes Native e Host não estão eleitos a servirem como objetos base.

Selecionado o objeto base, agora voce pode criar os objetos que herdarão e modificarão o objeto base conforme os requisitos. Os objetos filhos criados pelo desenvolvedor herdam todas as propriedades e métodos do objeto base, incluindo o construtor e as implementações de métodos. Lembrando que todas as propriedades e métodos são públicos, então os objetos podem acessá-los diretamente. Esses objetos podem adicionar novas propriedades e métodos não presentes no objeto base ou reescrever com novas implementações.

Keyword This

A palavra chave This faz referência ao escopo do objeto onde ela está sendo chamada.

Como o uso do This no ECMAScript existe a possibilidade de emular os escopos privados usando a palavra chave var, já que por default a linguagem só conhece o escopo público.

function objetoY()  {
    var variavel_privada = "Essa variável é privada";
    this.variavel_publica = "Essa variável é pública";
}

Nesse código a variável denominada “variavel_privada” não é propriedade do objeto “objetoY” e sim uma propriedade local ao contexto desse objeto, simulando assim uma variável privada já que outros contextos não terão acesso a ela, apenas métodos e propriedades internas ao objeto, enquanto a propriedade “variavel_publica” faz parte do objeto e é de escopo público.

Herança via Object masquerading

É uma estratégia que emula a herança, um construtor associa todas as propriedades e métodos (com o paradigma de declaração de Construtor) usando a keyword “this” para o contexto interno do qual foi referenciado. Porque o construtor é como uma função, voce pode montar o construtor de um objeto “A” dentro de um método de um objeto “B” e chamá-lo. O objeto “B” então recebe as propriedades e métodos definidos no construtor de “A” porque o this agora aponta para o contexto do novo objeto.

Cria o objeto que servirá de base:

function A(sColor) {
    this.color = sColor;
    this.sayColor = function () {
        alert(this.color);
    };
}

Agora eu crio o objeto chamado de “B” que herdará de “A”, quando eu chamo o objeto “A”, ele é referenciado ao contexto interno e o “this” passa a pertencer a “B”:

function B(sColor, sName) {
    this.newMethod = A;
    this.newMethod(sColor);
    delete this.newMethod;
    this.name = sName;
    this.sayName = function () {
        alert(this.name);
    };
}

Ao executar essas sentenças:

var objA = new ClassA('red');
var objB = new ClassB('blue', 'Nicholas');
objA.sayColor(); //outputs 'red'
objB.sayColor(); //outputs 'blue'
objB.sayName(); //outputs 'Nicholas'
alert(objB instanceof A ); //false
alert(objB instanceof B ); //true

Temos as mesmas chamadas de A em B com emulação completa da herança.

Como perceberam, Object Masquerading suporta a implementação de múltipla herança, mas como se trata de uma estratégia de emulação, essa abordagem não define o tipo do objeto que herda semelhante aos tipos herdados e portanto falha ao tentar comparar pelo “instanceof“.

Herança via métodos call() e apply()

Os métodos globais call e apply (que todo objeto herda do objeto Global) são estratégias semelhantes ao “Object Masquerading” e importam o contexto de um objeto ao outro.

call(Objeto, argumento1, argumento2, ...)
apply(Objeto, array-de-argumentos)

Se diferenciam somente no quesito que se refere aos parâmetros passados, onde o método call recebe uma sequencia de parâmetros, enquanto o método apply recebe um array de parâmetros.

function nome(sPrefixo, sSufixo) {
    alert(sPrefixo + this.parametro_interno + sSufixo);
};
var obj = new Object();
obj.parametro_interno = 'Milfont';
nome.call(obj, 'O ', ' é o mais elegante. ');

Observe que nesse exemplo a função “nome” está definida fora do objeto “obj” e associa a propriedade parametro_interno a seu próprio contexto, utilizamos o método call para passar o objeto “obj” ao contexto da função “nome” e a sequência de parâmetros esperados pelo seu construtor.

A função apply se comporta da mesma maneira e difere apenas na execução:

nome.apply(obj, new Array('O ', ' é o mais elegante. '));

Ao invés de passar os parâmetros em sequência, passamos um array com a sequência desses parâmetros.

A vantagem do método apply é que toda função tem uma propriedade chamada arguments que herda do objeto Global e facilita a implementação:

function A(sNome) {
    this.nome = sNome;
    this.digaNome = function () {
        alert(this.nome);
    };
}
 
function B(sNome, sName) {
    A.apply(this, arguments);
    this.sobrenome = sName;
    this.digaSobrenome = function () {
        alert(this.sobrenome);
    };
}

Aqui temos dentro da função “B” a chamada de “A” passando pelo método apply o contexto de “B” para o interior da função “A”, observe que repassa a propriedade arguments que é um array de argumentos recebidos.

Executando, teríamos:

var objA = new A('martins');
var objB = new B('Christiano', 'milfont');
objA.digaNome();  // 'martins'
objB.digaNome();  // 'Christiano'
objB.digaSobrenome(); // 'milfont'

Herança via Prototype

A forma de herança atualmente reconhecida pela especificação é a cadeia de protótipos ou Prototype chaining. Utilizando a propriedade prototype que todos os objetos herdam de Global, voce associa o contexto (métodos e propriedades) a outro objeto e o qualifica a ser do mesmo tipo do objeto que ele está recebendo.

function A() {}
A.prototype.nome = 'red';
A.prototype.digaNome = function () {
    alert(this.nome);
};
function B() {}
B.prototype = new A();

Aqui a função B recebe todos os métodos e propriedades de A e passa a ser do tipo dela, executando teríamos:

var objA = new A();
var objB = new B();
objA.nome = 'martins';
objB.nome = 'Christiano';
objA.digaNome(); // martins
objB.digaNome(); // Christiano
 
alert(objB instanceof A); // true
alert(objB instanceof B); // true

Formas híbridas podem ser usadas, mesclando esses métodos na construção dos objetos.

Multipla herança

Algumas linguagens orientadas a objetos habilitam o suporte à múltipla herança. Que é a capacidade de um objeto herdar as propriedades (métodos e atributos) de mais de um objeto. Além que esse objeto passa a ser da espécie dos objetos que ele herda, como por exemplo:

function Passaro(){
    this.nome = 'papagaio';
}
function Produto(){
    this.valor = 'R$10,00';
}
 
Passaro.prototype = new Produto;
Passaro.prototype.imprimir = function(){ 
    return this.valor + ' - ' + this.nome;
}

Uma classe PetStore poderia realizar um teste se o periquito é dos tipos Produto e Passaro para aplicar medicamento adequado à espécie por exemplo, esse tipo de construção é comum em linguagens como C, é simulado em Java por meio de interfaces.

var passaro = new Passaro();
alert(passaro instanceof (Produto)); // saida: true
alert(passaro instanceof (Passaro)); // saida: true
alert(passaro.imprimir()); // saida: "R$10,00 - papagaio"

Javascript não suporta múltipla herança pelo método de prototype que é o método que define o tipo do objeto, existe entretanto como contornar isso, um exemplo é a biblioteca zInherit (http://www.nczonline.net/downloads/) de Nicholas C. Zakas (http://www.nczonline.net/writing/), autor dos excelentes livros “Professional Ajax” e “Professional JavaScript for Web Developers”.

Esse script acrescenta os métodos inheritFrom e instanceOf ao objeto nativo Object.

function Biologia(){
    this.reino = 'animal';
}
function Produto(){
    this.valor = 'R$10,00';
}
function Passaro(){
    Produto.apply(this);
    Biologia.apply(this);
    this.nome = 'periquito';
}
 
Passaro.prototype.inheritFrom(Produto);
Passaro.prototype.inheritFrom(Biologia);
Passaro.prototype.imprimir = function(){
    return this.reino + ' - ' + this.valor + ' - ' + this.nome;
}
 
var passaro = new Passaro();
alert(passaro.instanceOf(Produto)); //saida : true
alert(passaro.instanceOf(Biologia)); //saida : true
alert(passaro instanceof (Passaro)); //saida : true
alert(passaro.imprimir()); // saida: "animal - R$10,00 - papagaio"

O metodo apply() habilita a aplicar um método de outro objeto no contexto de um objeto diferente.

Os frameworks modernos como ExtJS e YUI tem suas formas de implementarem a múltipla herança. Uma das formas mais utilizadas pelos Frameworks Ajax é copiarem os métodos e variáveis de um protótipo ao objeto que herdará.

Posted in JavaScript, Linguagens, Orientação a Objetos, Web Development ~ 4 Comments

Adicionar ao Rec6