sexta-feira, 2 de novembro de 2007

Estruturas de dados em Javascript

Aqui temos mais um caso típico de código Javascript:
for (i=0;i < arrParameterRange.length ; i++)
{
// set the temporary variables, to improve the code readability
sParamFormat = arrParameterRange[i][0];
sParamRangeFrom = arrParameterRange[i][1];
sParamRangeTo = arrParameterRange[i][2];
...
alert( sParamFormat + " for range " + sParamRangeFrom + " : " + sParamRangeTo );
...
}
O que me levou a escrever sobre este código foi o comentário que lhe está associado.
De facto a intenção é boa, mas estão a tentar resolver o problema errado.

O problema deste código reside na forma como os dados estão a ser guardados, nomeadamente estão a usar Arrays de Arrays.
A inicialização típica destes arrays é algo como o código seguinte:
var arrParameterRange = new Array();
arrParameterRange[0] = new Array( 'DumbFormat', 'DumbFrom_0', 'DumbTo_0' );
arrParameterRange[1] = new Array( 'DumbFormat', 'DumbFrom_1', 'DumbTo_1' );
...
E isto depois implica código semelhante ao que vimos no início, com indices HardCoded por todo o lado, que não é nada fácil de ler, expecialmente quando temos sub Arrays com dimensão 10 ou mais, pois nós humanos perdemos a capacidade de associar eficazmente os indices com os dados.

Curiosidade: Está provado experimentalmente que o limite do humano médio é de 7 itens. Isto é relevante em questões relacionadas com usabilidade (número máximo de opções de um menu, por exemplo), assim como em outras aplicações.
alert( arrParameterRange[i][2] + " : " + arrParameterRange[i][3] );
Existem alguns programadores mais iluminados que tentam dar a volta ao problema, definindo "constantes" para usar para os indíces, mas novamente estamos a resolver o problema errado.
var RANGE_IDX_FORMAT = 0;
var RANGE_IDX_FROM = 1;
var RANGE_IDX_TO = 2;
...

alert( arrParameterRange[i][RANGE_IDX_FROM] + " : " + arrParameterRange[i][RANGE_IDX_TO] );
O problema que deviamos estar a resolver, era como estruturar correctamente os dados. E para isso temos objectos em Javascript, que servem a sua função, mas que também servem para dar nomes aos dados, nomeadamente às propriedades do objecto. Isto porque Objectos sem métodos são equivalentes a estruturas (dado que é tudo público, em Javascript).

Então vamos definir um objecto em Javascript para guardar os nossos dados:
var ParamRange = function( format, from, to )
{
this.format = format;
this.from = from;
this.to = to;
}
E com esta simples alteração, já conseguimos código legível e fácil de manter:
var arrParameterRange = new Array();
arrParameterRange[0] = new ParamRange( 'DumbFormat', 'DumbFrom_0', 'DumbTo_0' );
arrParameterRange[1] = new ParamRange( 'DumbFormat', 'DumbFrom_1', 'DumbTo_1' );
...

for( i=0; i < arrParameterRange.length ; i++ )
{
var paramRange = arrParameterRange[ i ];
...
alert( paramRange.format + " for range " + paramRange.from + " : " + paramRange.to );
...
}
Claro está que o melhor seria mesmo definir métodos para isolar as propriedades do objecto, e providenciar as funcionalidades necessárias, mas este tema já vai longo e ainda tinha muito que escrever se fosse por ai.

Sem comentários: