@rodbv

Slides e vídeo da minha palestra sobre JavaScript para o DotNetFloripa

fazer um comentário »

Desde que me mudei para Florianópolis participo do grupo DotNetFloripa, que além de promover discussões online sobre mercado e tecnologia, também tem palestras a cada 1 ou 2 meses. Esse mês foi minha vez de estrear como palestrante, fugindo um pouco do tema central do grupo (.NET) e abordando JavaScript, aquela linguagem que todos usam e poucos estudam :)

O pessoal do SENAI-SC, além de ceder o excelente auditório, também gravou a palestra dessa vez. Divirta-se, e me contate caso tenha alguma dúvida, correção etc.

Video:

Slides:

 

Abraços!

Escrito por rodbv

15/11/2011 em 15:11

Publicado em Javascript, Programação

Vamos dar uma olhada no YUI3

com 3 comentários

 

Olá,

Hoje vou falar um pouco sobre YUI3, a última versão da biblioteca Javascript e CSS open-source do Yahoo. A biblioteca é gigantesca, então vou me ater a aspectos que considero mais úteis no meu dia-a-dia como usuário de longa data dessa biblioteca.

O que não pretendo fazer nesse e nos próximos posts é comparar extensivamente o YUI com jQuery e outras bibliotecas Javascript, a não ser que seja para tornar mais claro algum aspecto de sintaxe ou funcional. Política, futebol, religião e escolha de framework não se discute! :)

O que é YUI3

YUI significa Yahoo! User Interface, e é uma coleção de controles e utilitários para DOM e CSS, mantido e usado extensivamente pelo Yahoo em sites como Flickr e YahooMail, e publicado sob licença BSD. A premissa da biblioteca é oferecer suporte cross-browser consistente, incluindo smartphones e dispositivos com interface touch. Basicamente tudo o que um desenvolvedor front-end web precisa é oferecido: utilitários CSS tais como Reset, Fonts e Grid; bibliotecas Javascript para manipulação e uniformização do DOM, Ajax, JSON, carregamento dinâmico de recursos, animações, controles tais como editor de texto, calendário, overlay etc (além de facilidades para criarmos nossos próprios controles), e facilidades para desenvolvedores, uma ótima biblioteca de testes unitários (YUITest), logger e compressor.

 

image

Além desses módulos “oficiais”, o YUI Gallery oferece muitos módulos desenvolvidos por terceiros (287 no momento, e crescendo todo dia):

image

A versão 3 dessa biblioteca foi lançada no ano passado, e apresenta uma série de melhorias em termos de boas práticas, desempenho e simplicidade de uso, quando comparado com a versão 2. Como YUI3 foi criado do zero, muitos controles presentes do YUI2 ainda não foram reescritos, mas existe a biblioteca YUI2-in-3 que permite que usemos recursos do YUI2 dentro do YUI3.
A documentação, com vários exemplos e toda a API, pode ser encontrada em aqui. Outro recurso muito útil é o fórum de discussão, o Yahoo Developer Network e o YUI Theater, que tem vários vídeos (em inglês) sobre diferentes partes do YUI, além de Javascript em geral e até um toque de Node.js (aliás, YUI3 roda no Node.js, server-side tb!).

Colocando o YUI3 na sua página

Pra começarmos a usar o YUI3 basta colocar o seguinte script na sua página:

<script type="text/javascript" src="http://yui.yahooapis.com/3.3.0/build/yui/yui-min.js"></script>

 

Nesse caso estamos usando o servidor do Yahoo, mas o Google também hospeda a biblioteca. Além disso você pode baixá-la e carregá-la localmente, claro. Uma vantagem de usar o serviço do Yahoo é que eles oferecem um serviço de combo, isso é, permitem que você carregue vários arquivos da biblioteca com uma só requisição, reduzindo a latência (vamos ver mais sobre isso depois).

Carregando esse "core" do YUI você vai ter à sua disposição o objeto instanciável YUI(), que nos permite carregar dinamicamente todos os outros módulos que a gente queira usar, com suas dependências. O prefixo -min indica que quero baixar a versão minificada do arquivo javascript, ou seja, sem quebras de linha, espaços em branco ou comentários. Você também pode usar a versão -debug se quiser debugar o YUI (que joga várias mensagens pro console do browser), mas lembra de mudar pra -min quando for por em produção :)

<script>
    YUI().use("*", function (Y) {
        //YUI pronto pra usar!
    });
</script>

Note que o código fica dentro de uma função de callback, que é chamada quando todas as bibliotecas requisitadas forem baixadas. O objeto Y expõe todas as funções oferecidas por essas bibliotecas que estivermos utilizando. Isso evita o uso excessivo de variáveis e funções globais, e deixa nosso código dentro de um "sandbox" simples, o closure dessa função – variáveis e funções declaradas dentro desse callback não são vistos do lado de fora.


Um exemplo prático

Vamos criar então um exemplo simples (uma lista de compras) para explorar algumas das  bibliotecas mais comuns do YUI3.

<body>
    <ul id="lista">
        <li>Pound pastrami</li>
        <li>Can kraut</li>
        <li>Six bagels</li>
    </ul>
    <div>
        <input type="text" id="novoItem" />
        <input type="button" value="Adicionar" id="adicionar" />
    </div>
</body>

image

A primeira coisa que queremos fazer é adicionar o texto entrado na lista. Pra isso vamos usar a biblioteca node, que é uma biblioteca de manipulação do DOM. Essa biblioteca tem duas funções básicas, Y.one, que retorna um objeto Node do YUI, e Y.all, que retorna uma coleção de Nodes. A seleção, assim como no jQuery, é feita com uso de seletores CSS.

<script>
    YUI().use("node", function (Y) {
        var novoItem = Y.one("#novoItem").get("value");
        alert(novoItem);
    });
</script>

 

Veja que coloquei “node” no YUI().use: isso informa ao YUI que ele precisa buscar o script para Node (e suas dependências) lá no servidor. Quando estiver tudo baixado e instanciado, o código prossegue (dá uma olhada no tráfego no screenshot do browser):

image

Agora basta criar um novo elemento <li> e adicioná-lo à lista, quando o botão é pressionado. Para podermos detectar eventos na página, vamos usar outro módulo do YUI, Event.

<script>
    YUI().use("node", "event", function (Y) {
        Y.one("#adicionar").on("click", function () {
            //pega o valor da caixa de texto, sai se estiver vazia
            var novoItem = Y.one("#novoItem").get("value");
            if (!novoItem) return;

            //cria o nome e adiciona aa lista
            var li = Y.Node.create("<li>" + novoItem + "</li>");
            Y.one("#lista").append(li);

            //limpa a caixa de texto
            Y.one("#novoItem").set("value", "");
        });
    });
</script>

Animando um pouco

Vamos deixar a página um pouco mais dinâmica fazendo uma pequena animação quando um item é adicionado, fazendo-o aparecer aos poucos. Pra isso vamos precisar carregar a biblioteca anim, mudar um pouco o nosso <li> para que ele fique transparente a princípio,  e rodar a animação.

<script>
    YUI().use("node", "event", "anim", function (Y) {
        Y.one("#adicionar").on("click", function () {
            //pega o valor da caixa de texto, sai se estiver vazia
            var novoItem = Y.one("#novoItem").get("value");
            if (!novoItem) return;

            //cria o nome e adiciona aa lista
            var li = Y.Node.create("<li>" + novoItem + "</li>").setStyle("opacity", 0);
            Y.one("#lista").append(li);

            //limpa a caixa de texto
            Y.one("#novoItem").set("value", "");

            //prepara e roda a animacao
            var anim = new Y.Anim({ node: li,
                to: { opacity: 100 },
                duration: 5000
            });
            anim.run();
        });
    });
</script>

 

image

Repare que eu pude chamar o método setStyle do node encadeado  com o método create: todo método da biblioteca Node retorna o próprio node, o que permite esse encadeamento (chaining) e torna o código mais compacto.

Para a animação eu só precisei especificar a opacidade final e a duração (linhas 17-22)

Se eu quisesse evitar que o módulo anim fosse carregado se o valor de novoItem fosse inválido (em branco), eu poderia adiar o .use(“anim”) (linha 15):

<script>
    YUI().use("node", "event", function (Y) {
        Y.one("#adicionar").on("click", function () {
            //pega o valor da caixa de texto, sai se estiver vazia
            var novoItem = Y.one("#novoItem").get("value");
            if (!novoItem) return;

            //cria o nome e adiciona aa lista
            var li = Y.Node.create("<li>" + novoItem + "</li>").setStyle("opacity", 0);
            Y.one("#lista").append(li);

            //limpa a caixa de texto
            Y.one("#novoItem").set("value", "");

            Y.use("anim", function () {
                
                //prepara e roda a animacao
                var anim = new Y.Anim({ node: li,
                    to: { opacity: 100 },
                    duration: 5000
                });
                anim.run();
            });
        });
    });
</script>

 

A diferença é que nesse caso, eu faria um request a mais pro servidor, pra baixar o anim e suas dependências (último GET no Firebug)

image

Como mencionei lá em cima, o servidor do Yahoo fornece o serviço de combo, o que ajuda muito no desempenho da página. Pra comparar, vamos desabilitar o combo (fazendo YUI({combine:false})), e rodar de novo a página, pra ver o que acontece:

image

Praticamente o dobro do tempo pra carregar tudo, e um monte de requests acontecendo!

Então já estamos convencidos que usar o combo é uma boa. Agora, se você está criando uma página com vários componentes do YUI, e quer configurar o seu combo de forma granular, você vai querer usar o YUI Configurator.

No caso da nossa página, eu posso configurar pra carregar os módulos node, event e anim imediatamente:

image

Agora é só substituir a tag script na minha página com essa gerada pelo Configurator, voltar com o YUI().use(“*”) – já que tudo o que eu preciso já vai estar carregado – e checar que realmente tudo acontece com só 1 request agora, e ainda mais rápido

image

<script>
    YUI().use("*", function (Y) {
        Y.one("#adicionar").on("click", function () {
            //pega o valor da caixa de texto, sai se estiver vazia
            var novoItem = Y.one("#novoItem").get("value");
            if (!novoItem) return;

            //cria o nome e adiciona aa lista
            var li = Y.Node.create("<li>" + novoItem + "</li>").setStyle("opacity", 0);
            Y.one("#lista").append(li);

            //limpa a caixa de texto
            Y.one("#novoItem").set("value", "");

            //prepara e roda a animacao
            var anim = new Y.Anim({ node: li,
                to: { opacity: 100 },
                duration: 5000
            });
            anim.run();
        });
    });
</script>

Legal, não? Smile Assim você tem controle total sobre aquilo que deseja carregar sob premissa, e aquilo que deseja carregar sob demanda (talvez aquele drag-and-drop no cantinho da tela que quase ninguém usa).

Por enquanto é só! Abraços!

Escrito por rodbv

29/04/2011 em 17:57

Publicado em Javascript, Programação

Trabalha com JS? Então porque não aprende? :) (parte 2 – funções)

com 5 comentários

No post de ontem começamos a dar uma olhada geral na linguagem JavaScript, mas resolvi deixar para um post separado a parte central da linguagem, que são as funções.

A grande ‘sacada’ de quem começa a estudar JavaScript mais a fundo é entender que a linguagem é funcional, ou seja, funções são cidadãos de primeira-classe, podendo ser tratadas como qualquer outro objeto, passados como parâmetro, podem ser o resultado de outra função, etc.

Provavelmente a segunda grande sacada é o uso de funções anônimas. Quem usa jQuery com certeza vê muito código do tipo

$('#botao1').click(function() {
  alert('Você clicou no botão 1.');
});    

Repare que o parâmetro passado para a função click é uma função sem nome; isso é uma função anônima. Todas bibliotecas modernas de JavaScript usam e abusam de funções anônimas, que são especialmente úteis para programação assíncrona (podemos passar uma função anônima como parâmetro de callback).

Podemos também atribuir uma funcão anônima a uma variável

var minhaFuncao = function(nome) {
    return "Olaaaa " + nome;
}
minhaFuncao("Enfermeira!"); //Olaaaa Enfermeira!

//A funcao anonima pode receber um nome
var minhaFuncao2 = function mf(nome) {
    return "Bom dia " + nome;
}
minhaFuncao2("camarada"); //Bom dia camarada

Como vimos acima, a função anônima pode também receber um nome (por mais estranho que isso soe). Isso pode não parecer muito útil a princípio, mas nos ajuda quando lidamos com funções recursivas (afinal, como a função poderia chamar a ela mesma, se ela não tem nome?)

var fatorial = function fat(x) {
    return x == 1 ? 1 : x * fat(x-1);    
}

Antes que eu me esqueça, também podemos declarar funções no estilo tradicional:

function minhaFuncao(nome) { 
    return "Olaaaa " + nome; 
}    
minhaFuncao("Enfermeira!"); //Olaaaa Enfermeira!

..e podemos declarar funções dentro de funções:

function grita (nome) {
    function maiusculo(txt) {
        return txt.toUpperCase();
    }        
    return "OLA " + maiusculo(nome) + " ADORO CAPS LOCK!!!";
}
alert(grita("amigao")); //OLA AMIGAO ADORO CAPS LOCK!

Naturalmente que a função maiusculo será acessível apenas dentro da função grita. Mas uma coisa interessante é que, dentro de um bloco de escopo, a função pode ser declarada em qualquer lugar, então se eu fizer o seguinte também funciona

function grita (nome) {
    return "OLA " + maiusculo(nome) + " ADORO CAPS LOCK!!!";

    function maiusculo(txt) {
        return txt.toUpperCase();
    }        
}
alert(grita("amigao")); //OLA AMIGAO ADORO CAPS LOCK!

Como vimos no post sobre closures, esse padrão de funções-dentro-de-funções nos permite criar pseudo-classes, com métodos privados.

Mais uma curiosidade sobre funções em JavaScript: como funções são objetos, elas podem ter atributos. Isso nos permite implementar memoização de forma bem simples. Funções memoizadas são funções que lembram valores previamente computados, num cache (memo) local. Se por exemplo eu tenho uma função que calcula primos, eu posso evitar de recalcular primos que já foram calculados:

function primo(x){
   //se a funcao ainda nao tem o memo, cria
   if (!primo.memo)
     primo.memo = [];

   //checa se o memo ja possui o valor computado,
   //se sim, retorna esse valor
   if (primo.memo[x] != undefined)
     return primo.memo[x];

   //nao temos o valor computado, bora computar entao...
   var ehPrimo = true;
   for(var i = 2; i < Math.sqrt(x); i++) {
    if (x % i == 0) {
       ehPrimo = false;
       break;
    }
   }

   //guarda o valor no memo...
   primo.memo[x] = ehPrimo;

   // ...e retorna
   return ehPrimo;
}

Argumentos

Funções JavaScript podem ser chamadas com qualquer número de argumentos, independente de quantos foram realmente especificados na declaração da função. Argumentos não utilizados ficam com o valor undefined.

Podemos também criar funções que aceitam um número qualquer de argumentos (funções variádicas), igual ao que se obtem com o uso de params[] em C#. Para isso basta acessarmos a variável implícita arguments, disponível em qualquer função:

function escreve(){
  var ret = "";
  for (var i = 0; i < arguments.length; i++)
     ret += arguments[i] +";"
  return ret;
}
alert(escreve(1,2,3)) //1;2;3

A variável arguments pode ser acessada por índice (arguments[0]) e pode ter o seu comprimento checado (arguments.length), mas não é um array verdadeiro, e sim um objeto do tipo Arguments. Lembre-se disso caso você veja algum erro inesperado na hora de manipular arguments (um dia isso vai acontecer, pode ter certeza). Douglas Crockford, um dos membros do comitê da linguagem JavaScript, afirmou numa palestra que isso foi um vacilo na criação da linguagem JavaScript, e que pretendem consertar no futuro.

Lembra que eu disse que precisávamos dar um nome a uma função anônima para poder torná-la recursiva? Na verdade, dá pra fazer isso também usando um outro atributo de arguments, que é arguments.callee. Então eu poderia fazer algo do tipo return x * arguments.callee(x-1); mas confesso que eu nunca achei outro uso para esse atributo.

Outra dica interessante é a seguinte: quando você tiver uma função com muitos parâmetros, é melhor beneficiar-se do fato de ser muito fácil criar objetos literais em JavaScript. Compare os dois métodos abaixo, que copia parte de um array para dentro de outro, pra ver qual é mais legível:

var a = [1,2,3,4,5,6,7,8,9];
var b = [100, 200, 300, 400]

//versao 1: com varios parametros
function copyArray(from, from_start, from_end, to, to_start) {
    // ....
}
copyArray(a, 0, 3, b, 0);

//versao 2: com um objeto contendo parametros
function copyArray2(args) {
    var from = args.from,
        from_start = args.from_start || 0;
        //....
}
copyArray2({from: a, to: b, from_end:3});

Contexto

Outro parâmetro implícito em toda função é o parâmetro this. Esse parâmetro, quando mal entendido, pode causar uns bugs bem chatinhos, mas a boa notícia é que não é difícil entender como ele funciona. Por outro lado, quando usados de forma inteligente ele permite tornar funções ainda mais flexíveis.

O parâmetro this é uma referência ao objeto a partir do qual a função está sendo chamada.

Moleza né? Ótimo. Por exemplo, se eu fizer o seguinte código

<input type="button" id="botao1" onclick="alert(this.id)" value="click me"/>

..quando eu apertar o botão, eu serei saudado com o id do mesmo. Sem mistério.

Isso fica legal quando eu uso uma biblioteca tipo jQuery, que me permite associar uma função a vários controles de uma vez:

<input type="button" id="botao1" value="click me"/>
<input type="button" id="botao2" value="click me"/>
<input type="button" id="botao3" value="click me"/>
...
<script>
​$(function(){
  $("input:button").click(function() {
    alert(this.id);
  });
})​
</script>

Cada botão que eu apertar vai me mostrar o seu id. Agora, se uma função é rodada sem ser parte de um objeto, this vai se referenciar ao objeto global. No caso de uma página html, isso será o objeto window.

Entendendo o básico do uso do this, podemos aprender a usar dois métodos das funções (funções são objetos, então também podem ter métodos, lembra?): call e apply.

O call me permite chamar uma função especificando qual vai ser o objeto this dela; veja o exemplo abaixo

function mudaCor(cor){
   this.style.background = cor;
}

mudaCor.call(botao1, "red"); //this dentro de mudaCor vai ser botao1, que fica vermelho...
mudaCor.call(botao2, "green"); //..e assim por diante
mudaCor.call(botao3, "blue");

O segundo método, apply, é parecido, mas ele me permite passar um array de argumentos para a função:

function mudaCor(cor, largura, altura){
   this.style.background = cor;
   this.style.width = largura;
   this.style.height = altura;
}

mudaCor.apply(botao1, ["red", "120px", "20px"]);

Se eu estiver dentro de outra função eu também posso fazer função.apply(objeto, arguments), passando os argumentos recebidos à frente.

Como funções são objetos, eu posso sobrescrever uma função já existente simplesmente assinalando um novo valor para ela. Embora seja perigoso fazer isso em qualquer lugar, podemos tomar vantagem disso e de closures para criar um override de uma função do DOM:

document.createElement = (function (fn)
{
    return function (type, id, className)
    {
        var elem = fn.call(document, type);
        if (id) elem.id = id;
        if (className) elem.className = className;

        return elem;
    };
})(document.createElement);

Vamos entender o que rolou aqui: a função document.createElement original aceita apenas um parâmetro, o tipo de objeto (div, span etc), mas eu quero criar uma nova versão da função que aceite também o id e a classe do elemento.

Então eu assinalei o document.createElement a uma função anônima auto-executável, passando como parâmetro a função original (linha 11). Ao ser executada essa função retorna uma nova função, que aceita os três parâmetros que eu quero ter, e que atribui id e className, caso eles tenham sido passados. Eu uso call na linha 5 para garantir que o contexto dessa nova função será o mesmo da função original, no caso, document.

Vale a pena perder um tempinho tentando entender o que aconteceu aqui :) Essas 11 linhas mostram bem o quão flexível são funções em JavaScript!

Escrito por rodbv

17/09/2010 em 21:53

Publicado em Javascript, Programação

Trabalha com JS? Então porque não aprende? :) (parte 1)

com 3 comentários

Photoshop toscoOntem escrevi sobre closures com JavaScript, e o post foi muito bem recebido. A maioria dizendo que já usava isso de uma forma ou outra, só não sabia o nome e a definição.

Aliás isso é algo muito comum tanto em JavaScript quanto em CSS: todo mundo usa, mas pouca gente realmente fala “vou parar para estudar isso”. Nosso foco costuma ser na tecnologia do lado do servidor, C#, Java, etc. No lado do cliente a gente se vira e vai tocando! :)

O objetivo desse post dessa série, então, é dar um tour rápido e rasteiro nas características da linguagem JavaScript, para aprendizado ou revisão. A maior parte do que vou escrever aqui eu tirei direto de dois livros: JavaScript – The Definitive Guide, e JavaScript: The Good Parts, ambos da O’Reilly. O segundo é um livro bem fino, menos de 200 páginas, e simplesmente imperdível.

Números

JavaScript só tem um tipo de número: não diferencia entre float, int, decimal, nada. Tudo é armazenado como ponto-flutuante de 64 bits, equivalente ao double de Java e C#. Logo, pro JavaScript 11 e 11.0 são a mesma coisa. Como o Crockford alertou na palestra dele na QConSP, isso também significa que 0.1 + 0.2 não é exatamente igual a 0.3 (0.1 + 0.2 == 0.3 retorna falso). Cuidado com isso!

Podemos representar números com notação cientifica, logo 1000 pode ser escrito como 1e3.

A linguagem também oferece o objeto Math com operações matemáticas como Math.floor, Math.PI e Math.random().

A função parseInt serve para converter uma string para número; mas cuidado com erros como o abaixo:

parseInt("123") //123
parseInt("010") //8, WTF???
parseInt("010", 10) //10 

Se o primeiro caracter na string for zero, parseInt vai supor que a base a ser usada é octal, não decimal. Então é sempre bom usar o segundo parâmtro da função, que define a base.

O valor NaN é especial: significa Not a Number, ainda que se fizermos typeof(NaN) retorne “number”. Coisas de JavaScript.
NaN é produzido quando tentamos converter um valor inválido para numérico:

parseInt("Ola", 10) //NaN

O NaN também é contagioso: NaN+10 resulta em NaN. A função isNaN(valor) informa se o mesmo é ou não NaN. Já se fizermos uma divisão por zero, teremos o resultado “Infinity”. Um número negativo dividido por zero retorna “-Infinity”. Infinity também é contagioso.

Strings

Strings podem ser delimitadas por aspas duplas ou simples. Não existe o tipo char, então um caracter é simplesmente uma string de comprimento 1.

Podemos especificar caracteres unicode com \u, então “A” pode ser escrito como “\u0041”.

Como strings são quase-objetos (veremos mais sobre isso depois), podemos fazer coisas do tipo

"Bom dia".length //7
"Crock"[4] //k
"Javascript".charAt(2) //v
"Hello".toUpperCase().substring(1,3) //EL

Valores booleanos, truthy e falsy

Os valores booleanos no JavaScript são true e false. Mas qualquer valor pode ser colocado em uma expressão booleana e ser avaliado como “meio-que-verdadeiro” ou “meio-que-falso”, ou, para usar um termo muito comum em textos sobre JavaScript, truthy e falsy.

Ou seja, os valores abaixo, quando fazem parte de uma expressão booleana, resultarão em false, ou seja, são falsy:

    
    true && 0 //false
    true && NaN //false
    true && "" //false
    true && null //false
    true && undefined //false
    

Por isso que vemos muito código do tipo if (!valor) {…} ao invés de if (valor==null) {…}.
Todos os outros valores são avaliados como truthy.

Se temos um valor qualquer e queremos armazenar o seu valor booleano, podemos então fazer uma dupla negação

!!"" //false
!!"a" //true

Declaração de variáveis e escopo

Declaramos uma variável com o comando var. Mas, se esquecermos de colocar var, essa variável simplesmente será declarada como global. Isso é a causa de muitos bugs, então sempre lembre de usar var, mesmo que você queira (sabe-se lá porquê) ter uma variável global mesmo.

Uma variável declarada mas sem valor atribuido tem o valor undefined.

Uma pegadinha comum no JavaScript é sobre o escopo de variáveis declaradas dentro de um bloco. Observe o código abaixo:

if (true) {
  var x = 12;
}
alert(x) //12

Em linguagens como C#, teríamos um erro ao tentarmos acessar a variável x fora do if: qualquer variável declarada dentro de um bloco com chaves { e } tem escopo local a esse bloco; não em JavaScript! O escopo de uma variável em JavaScript é sempre o mesmo: a função dentro da qual ela foi declarada. E não faz diferença declarar todas as variáveis no início da função ou próximo de onde as mesmas são usadas, a alocação de memória será o mesmo. Por isso recomenda-se, por questão de clareza, que todas variáveis sejam declaradas no topo da função.

Objetos

Os valores simples da linguagem JavaScript são number, string, boolean, null e undefined. Todo o resto são objetos. Números, strings e booleanos podem ser tratados como objetos, por terem métodos, mas são imutáveis.

No JavaScript objetos são simplesmente uma coleção de chaves e valores, tal como dicionários ou hashes em outras linguagens. Como objetos podem ser valores de outros objetos, fica fácil criar um grafo de objetos.

Objetos também possuem protótipos, que veremos mais tarde.

Como objetos são simplesmente uma coleção de valores, podemos inicializar um objeto assim:


var meuObjeto = {};

var pessoa = {
    nome: "Rodrigo",
    emails: {
        trabalho: "rodrigo.vieira@trabalho.com",
        casa: "rodrigov@casa.com.br"
    }
};

Depois de criado podemos adicionar livremente novos atributos a um objeto. Podemos referenciar atributos com duas notações: usando ponto ou colchete. A segunda notação permite que usemos valores definidos em tempo de execução, bem como valores inválidos com a notação com ponto

pessoa.telefone = "8832-3843";
pessoa["#"] = "qualquer coisa"; //pessoa.# daria erro

É possível iterar sobre todos os atributos de um objeto:

var pessoa = { nome: "Rodrigo", telefone: "3727-4882"};

for (var atributo in pessoa) {
    alert(pessoa[atributo]); //Rodrigo, 3727-4882
}

Se a gente tenta acessar um atributo que não foi declarado em um objeto (por exemplo pessoa.HJgh), não teremos erro de runtime, simplesmente retornará undefined.

Outro aspecto interessante sobre objetos é a forma como ocorre herança, por protótipos. Mas isso fica para um post separado.

Vetores

Vetores (Arrays) em JavaScript não são arrays “de verdade”, ou seja, não são uma alocação linear de memória de acesso rápido. No JavaScript arrays são basicamente objetos em que as chaves são números inteiros sequenciais, iniciando com zero. Arrays são declarados e usados assim:

var a = [];
a[0] = "Hello";
a[1] = "World"

alert(a[3]); //undefined

Podemos misturar tipos diferentes em arrays:


var b = ["x", 4, { nome: "Rodrigo"}]

O tamanho de um array é sempre definido de acordo com o índice do último elemento presente. Repare no código abaixo:

var x = [];
x[50] = "Steve Jobs";
alert(x.length); //51
alert(x[0]); //undefined

Podemos adicionar um elemento ao fim de um array de duas formas

var x = [];
x[x.length] = "Linus"; //primeira forma
x.push("Torvalds"); //segunda forma
alert(x[0]); //Linus
alert(x[1]); //Torvalds

…e podemos retirar elementos de duas formas

var x = ["Bill", "Gates", "Steve", "Jobs"];
x.pop(); //primeira forma
alert(x.length); //3

x.splice(1,1); //segunda forma -> ["Bill", "Steve"]

x.delete(0); //[undefined, "Steve"];
alert(x.length); //2

O método splice aceita dois argumentos: o primeiro informa o índice a iniciar a retirada de valores, e o segundo o comprimento dessa retirada. Logo se fizermos x.splice(0, x.length) teremos um array vazio como resultado (mas seria mais fácil fazer x = [], claro!). Já o método delete transforma o item em undefined, mas não retira a posição do array. Deixa apenas um “buraco” com undefined.

Quando queremos iterar sobre os elementos de um objeto, vimos que podemos usar o comando for…in, mas no caso de arrays isso não deve ser feito. Isso porque esse tipo de iteração não garante a ordem na qual tais itens serão trazidos, e pode trazer atributos que fazem parte do protótipo de array.

Então o jeito certo de iterar em um array é igual em linguagens como C:

x = [ 1, 2, 3, 4];
for (var i=0; i<x.length; i++){
    //....
}

//podemos melhorar um pouco o desempenho guardando o valor computado de x.length
for (var i=0, len=x.length; i<len; i++){
    //....
}

Bem, por hoje é só! Na parte 2 veremos o componente mais importante do JavaScript, funções, bem como herança por prototipação, que é bem interessante.

Escrito por rodbv

17/09/2010 em 01:46

Publicado em Javascript, Programação

Javascript pra gente grande: entendendo closures

com 16 comentários

Crock NorrisNo fim-de-semana passado estive na QCon, evento muito legal que contou com a presença do “guru” do Javascript, Douglas Crockford.

Ele deu uma geral no passado e futuro da linguagem Javascript Ecmascript, e um dos slides que mais me chamou dizia o seguinte (tradução minha):

Escrever código ECMAScript sem entender closures é como escrever código Java sem entender classes.

Mensagem mais clara, impossível. E eu concordo. Javascript é uma linguagem que quase ninguém realmente senta pra estudar: a maioria das pessoas simplesmente assume que ela é meio parecida com Java (Java+script, certo?), e assim vai, escrevendo funções e declarando variáveis.

Mas para quem realmente se dedica a estudar Javascript um pouco mais a fundo, descobre uma bela linguagem, altamente funcional (no sentido F# da coisa) e expressiva.

Pra podermos tirar vantagem dessa linguagem, é importante entendermos o tripé conceitual que forma o Javascript:

  • Funções
  • Objetos
  • Closures

Vamos então dar uma olhada nesses três elementos:

Funções

No Javascript, funções são cidadãos de primeira classe. Isso significa que você pode assinalar uma função a uma variável, passar uma função como parâmetro em outra função, ou retornar uma função a partir de outra função, sem complicações sintáticas.

//funções como variáveis
var fn = function(mensagem){
    alert(mensagem);
};
fn("Ola"); //"Olá"

//função como parâmetro
function chamaFuncao(funcao, texto){
    funcao(texto);
}
chamaFuncao(alert, "Ola de novo"); //Olá de novo

//funcao sendo retornada em outra funcao (gerador de funcoes)
function geraSomador(numero){
    return function(x) { return x + numero;};
}
var soma5 = geraSomador(5);
alert(soma5(4)); //"9"

Objetos

Objetos em Javascript são simplesmente uma coleção de chaves e valores. Tudo em Javascript são objetos: funções, arrays, expressões regulares etc.

O fato de objetos serem apenas uma coleção de chaves e valores fazem que a sintaxe de criação seja bem simples:

var pessoa = {
     nome : "Douglas Crockford",
     profissao : "Guru Javascript"
};
alert(pessoa.nome); //Douglas Crockford
alert(pessoa["profissao"]); //Guru Javascript

Note também que eu posso acessar um atributo de um método tanto usando a notação com ponto, quanto usando a notação com colchete. A notação da linha 6 permite que eu acesse tais atributos dinamicamente, o que pode ser muito útil. Podemos também adicionar novos atributos a um objeto, depois de ele ter sido criado:

pessoa.email = "crock@yahoo.com";
pessoa["telefone"] = "+1 746 4840";
alert(pessoa.email); //crock@yahoo.com
alert(pessoa.telefone); //+1 746 4840
alert(pessoa.umAtributoQueNaoExiste); //undefined

Como o valor de um atributo pode ser qualquer objeto, podemos colocar funções e outros objetos ali:

var pessoa = {
    nome: "Douglas Crockford",
    dizAlgumaCoisa: function(coisa){
        alert(coisa);
    },
    telefones: {
        casa: "+1 434 3301",
        trabalho: "+1 434 9785"
    }
}

pessoa.dizAlgumaCoisa("Pode me chamar de Crock"); //Pode me chamar de Crock
alert(pessoa.telefones.casa); //+1 434 3301

Vendo o código cima já dá pra sentir um jeitão de orientação a objetos que tanto gostamos em C# e Java; mas o que temos em mãos é um objeto, como criamos algo que funciona como uma classe? Fácil, basta criarmos uma função que retorna objetos, e usarmos a palavra-chave new:

function Pessoa(nomeDaPessoa){
    return {
        nome: nomeDaPessoa,
        dizAlgumaCoisa: function(coisa){
            alert(coisa);
        },
        telefones: {
            casa: "+1 434 3301",
            trabalho: "+1 434 9785"
        };
    }
}

var crock = new Pessoa("Douglas Crockford");
crock.dizAlgumaCoisa(crock.nome);  //Douglas Crockford
crock.dizAlgumaCoisa(crock.telefones.trabalho); //+1 434 9785

Tá ficando legal, mas quando pensamos em classes, uma das primeiras coisas que vêm à cabeça é a possibilidade de termos atributos privados, e é aí que closures vêm nos ajudar.

Closures

Closure em inglês significa encerramento, no sentido de guardar, pôr em um lugar fechado. Repare no código que usamos lá em cima:

//funcao sendo retornada em outra funcao (gerador de funcoes)
function geraSomador(numero){
    return function(x) { return x + numero;};
}
var soma5 = geraSomador(5);
alert(soma5(4)); //"9"

Uma coisa muito curiosa aconteceu aqui: a minha nova função, soma5, de alguma forma lembra (encerra) o valor do parâmetro numero, depois de criada. Então tudo que declaramos dentro de uma função é privado, mas pode ser usado por membros públicos.

Resumindo então: closures são blocos de código que podem ser executados a qualquer momento, guardando o contexto no qual eles foram criados, mesmo quando a função que criou esse bloco já saiu de escopo.

..e isso obviamente vai cair muito bem pra gente criar uma classe bem encapsulada!

function Pessoa(nomeDaPessoa){
    //membros privados
    var _livros = [];
    var _ordenaLivros = function(){
        return _livros.sort();
    }

    //membros publicos
    return {
        adicionaLivro: function(livro) {
            _livros.push(livro);
            //return this;
        },
        meusLivros: function(){
            return _ordenaLivros(_livros).join(", ");
        }
    };
}

var p = new Pessoa("Crock Norris");
p.adicionaLivro("Javascript: The Good Parts");
p.adicionaLivro("Das Beste an Javascript");
alert(p.meusLivros()); //Das Beste an Javascript, Javascript: The Good Parts

Repare que o método adicionaLivro tem um return this comentado; se a gente descomentar essa linha, a função vai retornar uma referência ao próprio objeto. Isso permite encadeamento de métodos, para uma interface mais fluente:

    alert(p.adicionaLivro("Javascript: the good parts")
        .adicionaLivro("Das Beste an Javascript")
        .meusLivros()); //Das Beste an Javascript, Javascript: The Good Parts

Um outro uso legal de closures é evitar a criação desnecessária de variáveis globais (uma coisa ruim em qualquer linguagem). Podemos usar uma função anônima que é executada imediatamente (linha 8 ) para criar um bloco de código com suas variáveis locais, que saem de escopo assim que a função termina (ao invés de ficarem penduradas na página pra sempre) e evita conflitos com outras variáveis e funções da página:

//....
cor = "blue";
//...

(function(){
    var cor = "#FF0000";
    document.body.style.background = cor; //deixa o fundo da pagina vermelha
})();
alert(cor); //blue

Bem, por enquanto é isso :) Pretendo escrever mais sobre aspectos legais do Javascript, aceito sugestões.

Escrito por rodbv

16/09/2010 em 00:59

Publicado em Javascript, Programação

Em busca de uma arquitetura sólida – Parte 3

fazer um comentário »

Continuando nossa série sobre os princípios S.O.L.I.D. de programação orientada a objetos, hoje vamos ver a terceira letra da sopa, L. Caso você tenha acabado de achar esse blog, eu recomendo que leia antes a primeira e segunda partes.

O L em SOLID tem o nome mais esotérico de todos: Liskov Substitution Principle (LSP). Agora você já sabe o que falar amanhã no escritório pra impressionar seus colegas! :) O enunciado oficial desse princípio também é meio complicado, pois foi expresso de forma meio matemática:

Se, para cada objeto o1 de tipo S houver um objeto o2 de tipo T tal que para todos programas P definidos em termos de T, o comportamento de P é inalterado quando o1 é substituído por o2, então S é um supertipo de T.

OK, agora vamos traduzir isso pra português:

Um programa que usa uma determinada classe ou interface T deve poder usar objetos derivados de T sem ter conhecimento do tipo concreto desse objeto.

Talvez você já tenha ouvido o conselho de que é melhor programar em cima de abstrações, interfaces ou super-classes, pois isso nos permite maior flexibilidade, além de ajudar bastante na hora de testar o código. Um ótimo conselho, com certeza, mas se o LSP não for seguido, você pode encontrar bugs inesperados ou ser obrigado a escrever código que “cheira mal”, e que muito provavelmente também quebra os dois princípios que vimos antes, SRP e OCP.

Vamos ilustrar com um exemplo. Suponha que estejamos implementando um sistema para um banco, e como a gente sabe que terá que lidar com vários tipos de contas diferentes, vamos criar uma super-classe chamada ContaBase com a funcionalidade que você espera suportar pra qualquer tipo de conta:

 public abstract class ContaBase { public decimal SaldoAtual { get; private set; } public DateTime DataAbertura { get; private set; }</p><p style="clear: both"> </p><pre style="clear: both"><code>public virtual void AbreConta(decimal saldoInicial)
{
    SaldoAtual = saldoInicial;
    DataAbertura = DateTime.Now;
}

public virtual void EfetuaDeposito(decimal valor)
{
    SaldoAtual += valor;
}

public virtual void EfetuaSaque(decimal valor)
{
    SaldoAtual -= valor;
}
</code></pre><p style="clear: both"> </p><p style="clear: both">} 

A primeira classe concreta que você cria é para conta-corrente:

 public class ContaCorrente : ContaBase { //nada de especial nessa classe, vamos usar os metodos da superclasse } 

Como por enquanto a gente não vê nada de particular com essa classe, vamos deixar essa classe executar o código da super-classe sem mudanças.

Vamos então começar a usar essas classes em nosso código, num programa para o console

 class Program { public static void Main(string[] args) { //busca a conta bancaria Console.Write("Entre o numero da nova conta: "); string numeroConta = Console.ReadLine(); ContaBase conta = new ContaCorrente();</p><p style="clear: both"> </p><pre style="clear: both"><code>    //abre conta
    conta.AbreConta(1000);

    //efetua algumas operacoes...
    conta.EfetuaDeposito(25);
    conta.EfetuaSaque(319);

    //mostra o saldo
    Console.WriteLine("Saldo atual: " + conta.SaldoAtual);
    Console.ReadKey();
}
</code></pre><p style="clear: both"> </p><p style="clear: both">} 

Rodando o programa, parece que tudo funciona como esperado:

Ótimo, agora vamos começar a segunda iteração de desenvolvimento, e nosso cliente nos informa que precisa de uma conta especial, de investimento a médio prazo, e essa conta não pode aceitar saques caso tenha menos de 1 ano de criação.

Aqui está nossa nova classe:

 public class ContaInvestimento : ContaBase { public override void EfetuaSaque(decimal valor) { if (DateTime.Now.AddYears(-1) < DataAbertura) throw new Exception("Essa conta nao permite saque antes de " + DataAbertura.AddYears(1).ToShortDateString()); } } 

Vamos ajustar nosso programa pra lidar com esse tipo de conta. Vamos supor que contas-investimento sempre comecem com dígito 9 (o código que cria contas, claro, deveria estar numa factory ou algo do gênero, mas vamos manter o exemplo simples).

 class Program { public static void Main(string[] args) { //busca a conta bancaria Console.Write("Entre o numero da nova conta: "); string numeroConta = Console.ReadLine(); ContaBase conta; if (numeroConta.StartsWith("9")) conta = new ContaInvestimento(); else conta = new ContaCorrente();</p><p style="clear: both"> </p><pre style="clear: both"><code>    //abre conta
    conta.AbreConta(1000);

    //efetua algumas operacoes...
    conta.EfetuaDeposito(25);
    conta.EfetuaSaque(319);

    //mostra o saldo
    Console.WriteLine("Saldo atual: " + conta.SaldoAtual);
    Console.ReadKey();
}
</code></pre><p style="clear: both"> </p><p style="clear: both">} 

Tudo funciona bem com uma conta corrente, mas quando entramos uma conta de investimento, um erro ocorre:

Console 2b

Bem, nosso cliente não fica muito feliz com o “bang” saindo da caixa de som dele, e a gente se apressa em fazer um ajuste ali pela linha 18:

 conta.EfetuaDeposito(25); if (conta.GetType() == typeof(ContaInvestimento)) //checar aqui se a conta ja permite saque... conta.EfetuaSaque(319); 

Programadores iniciantes (ou distraídos, ou sob pressão) podem dormir com esse tipo de código (e vemos esse tipo de código aos montes), mas dá pra sentir que isso vai nos trazer problemas no futuro. O problema é que apesar de estarmos lidando com um objeto do tipo ContaBase, agora temos que checar se esse objeto é de um tipo específico para podermos efetuar saques. Ou seja, estamos seguindo o princípio de programar em cima de abstrações só pra “inglês ver”. Basta imaginar esse programa ampliado para 10 tipos de conta, cada um com suas particularidades, pra imaginar a dificuldade que vai ser manter esse código. A raiz do problema é que esse programa está violando o LSP: arquitetamos as classes de forma que agora a classe base não pode mais se substituída por qualquer subclasse, pois os resultados variam dependendo do tipo de subclasse que temos em mãos.

A forma mais fácil de detectar violações ao LSP, em C#, é procurarmos por código do tipo if(…typeof…). Um bom programa não deve ter que ficar checando tipos específicos: ou você programa diretamente em cima de uma classe final (logo, já sabe qual o tipo dela), ou se você está programando em cima de super-classes ou interfaces, o tipo não deve importar.

No nosso exemplo, uma forma simples de evitarmos tal problema e seguirmos o LSP seria criar um método PodeEfetuarSaque(), que, no caso de contas-investimento, checaria a data. Outras contas teriam outras regras de negócio, e contas sem qualquer tipo de restrição poderiam simplesmente retornar true. Com isso a regra de negócio volta para o lugar que ela pertence, dentro da classe para a qual ela se aplica. Nosso programa principal simplesmente faz uso de abstrações.

Até a próxima.


Escrito por rodbv

23/03/2009 em 20:39

Publicado em Programação

Um mês de TDD

com um comentário

No mês passado lançamos a nova versão do produto da nossa empresa, Confirmit, e depois de um ou dois dias respirando (ou seja, lendo blogs, experimentando com novas tecnologias) começamos a trabalhar na próxima versão.

Uma das decisões que tomei ao começarmos a versão 15.0(!) do nosso sistema foi adotar TDD (test-driven-development) em período integral. Eu já vinha escrevendo testes unitários há alguns anos, mas sempre escrevendo os testes ao fim do processo, pra checar o que eu já tinha desenvolvido. Com certeza isso é melhor do que nada mas não traz muitos dos benefícios do verdadeiro TDD, aquele em que a gente escreve um teste primeiro (sem código nenhum na aplicação), vê ele falhar e aí sim escreve o código que faz o teste passar, ou seja, o padrão vermelho-verde (o padrão tem esse nome em referência aos ícones vermelho e verde que costuma indicar o fracasso e sucesso da execução dos testes).

Algumas coisas que notei nesse período de 1 mês seguindo TDD:

  • No começo é difícil lembrar de escrever os testes antes: me peguei algumas vezes escrevendo um monte de código nas classes de regras de negócio antes de me tocar que eu deveria antes estar escrevendo testes para essas novas funções. O que fiz nesse caso foi comentar o código, escrever o teste, rodá-lo pra ver o teste falhar e aí retirar os comentários pro teste passar.
  • Minhas funções tornaram-se menores, mais atômicas: como a gente deve testar uma coisa de cada vez, em cada teste, eu me vi escrevendo funções menores que fazem somente o necessário para passar tais testes. Então ao invés de ter uma função que faz 3 coisas, passei a ter 3 funções que fazem 1 coisa, cada uma com um ou mais testes.
  • Passei a pensar mais em termos de API: como os testes são, de certa forma, o primeiro cliente de nossas novas funções, a gente se vê obrigado a pensar mais com o ponto de vista de um desenhista de APIs: funções com nomes mais descritivos, correspondendo aos testes específicos, com parâmetros e valores de retorno mais bem-pensados.
  • ASP.NET Webforms continua sendo horrível pra testar, mas com TDD fica mais fácil: como eu escrevo os testes antes de sequer abrir os projetos Web do ASP.NET, eu tive mais facilidade em realmente deixar toda a lógica de negócios na camada certa, e minhas páginas ASP.NET passaram a fazer mais da única coisa que elas devem fazer, que é chamar funções das camadas de negócio e mostrar os dados na tela. Quando eu não escrevia testes unitários, ou os escrevia a posteriori, era muito fácil acabar deixando minhas páginas ASP.NET “espertas” demais, fazendo coisas demais. Nesse aspecto, o uso do padrão de desenho de repositórios e serviços também têm sido muito úteis, mas isso fica pra outra postagem.
  • TDD é divertido! Essa talvez tenha sido a grande surpresa. Sinceramente eu não me lembro de me sentir tão motivado a começar o dia na frente do computador nos últimos anos. Ao seguir o padrão vermelho-verde eu me vi encarando a programação como uma espécie de jogo, onde eu me coloco uma tarefa/desafio pequena e a resolvo; coloco outra em cima dessa e a resolvo. A gente passa a quebrar os problemas em pedaços menores, mais digeríveis, e com isso a gente se sente menos sobrecarregado. Também é muito legal ver o relatório do servidor de CI ao fim do dia dizendo que todas minhas dezenas de testes passaram, dá uma sensação de dever cumprido. Como muitas vezes a gente leva meses pra ter feedback dos clientes quanto ao nosso trabalho, com TDD eu proporciono feedback instantâneo que me mantém motivado a continuar.
  • Confiança: sempre ouvi dizer isso de TDD, e é verdade: a gente se sente muito mais confiante. Posso fazer um grande refactoring no meu código, envolvendo ou não código de terceiros, e eu sei que, desde que todos os (milhares de) testes estejam verdes, eu não cometi nenhum erro extremamente estúpido. Ajuda muito a dormir tranquilo depois de um check-in envolvendo 100 mudanças de nome de variável ou de provedor de dados.
  • No começo a tentação a “pular” testes vai ser grande, mas persista: não tem muito a adicionar aqui, como toda mudança de hábito, no começo vai rolar uma vontade de dizer “ah não, dessa vez vou mandar ver sem testes, depois eu escrevo” mas a verdade é que a grande chance é que “depois” nunca aconteça, e esse código fique sem testes.

Eu sou meio cético a novas “ondas” que aparecem de vez em quando no mundo da programação, e confesso que quando ouvi falar em TDD há alguns anos também desconfiei que isso seria mais uma dessas ondas. Mas agora eu vejo que não é o caso, eu realmente acredito que, assim como programação OO, TDD veio pra ficar. Faz simplesmente muito sentido, e ao mesmo tempo é algo muito fácil de se adotar. Eu acho que dificilmente contrataria hoje em dia um desenvolvedor que se recusasse a escrever testes unitários. E você?


Escrito por rodbv

23/03/2009 em 20:13

Publicado em ASP.NET, Programação

Etiquetado com

Quatro livros sobre história dos computadores que todo programador deveria ler

fazer um comentário »

Quando ando pela minha empresa vejo vários livros sobre as tecnologias mais recentes (Ajax, ASP.NET MVC, DDD, Linq etc) nas mesas, mas nunca um livro sobre história de computadores, hardware e software. Eu acho a leitura desse tipo de livro extremamente motivante e esclarecedor: me ajuda a entender melhor o contexto geral do meu trabalho, o “big picture”, e também me anima a não desistir de encontrar soluções, pois vejo que mesmo os geniais “heróis” desses livros também ralaram muito, empacaram, pensaram em desistir mas no fim acharam as soluções que queriam. Me anima relembrar que no fundo é aquela velha história de 99% transpiração e 1% inspiração, e que então se eu não sou um gênio de QI 200 como um Turing ou Von Neumann, pelo menos posso compensar um pouco se eu me dedicar e me mantiver motivado.

Dentro desse espírito, aqui vão alguns livros que eu li nos últimos anos e que recomendo a todos geeks, em especial os que trabalham com computação, seja hardware ou software:

Where Wizards Stay Up Late: The Origins of the Internet
Todo mundo já ouviu falar que a Internet surgiu de um projeto do Departamento de Defesa americano, ARPANET. Mas em geral nosso conhecimento para por aí. Os autores desse livro entrevistaram várias pessoas envolvidas com esse projeto, e nos mostram como a idéia de conectar computadores evoluiu, é muito legal seguir a reconstrução da linha de pensamento que levou esses engenheiros a criarem pela primeira vez roteadores, servidores de nomes, RFCs, o protocolo TCP/IP, e mesmo aspectos triviais como o uso do @ em endereços eletrônicos.

The Dream Machine: J.C.R. Licklider and the Revolution That Made Computing Personal
Um personagem importante no livro anterior é J.C.R. “Lick” Licklider, que, sendo um psicológo e neurologista (embora o termo ‘neurologista’ não existisse na época), trouxe o aspecto humano, interativo às máquinas sendo desenvolvidas pelos matemáticos e engenheiros. Ele foi o primeiro a escrever sobre as possibilidades que computadores pessoais um dia poderiam trazer aos cidadãos comuns, numa época (anos 40, 50) em que todo mundo ainda via computadores como uma grande calculadora. Licklider escrevia sobre como computadores interconectados poderiam aumentar o QI de seus usuários, ao trazer respostas instantâneas, permitir associações de idéias e colocar pessoas em contato, algo que vemos hoje em dia com o Google. Ele também foi um dos pioneiros em defender o uso de interfaces gráficas, experiências intuitivas. Um livro um pouco mais longo que o Where Wizards…, mas recomendo se você assim como eu “devorar” aquele primeiro livro em poucos dias.

Rebel Code: Linux and the Open Source Revolution
Passando pro lado do software um pouco: esse livro eu li quando instalei o Mandrake Linux no minha máquina (lá pelos idos de 2001) e fiquei maravilhado com a qualidade do KDE, ao ver que realmente era possível existir software de grande qualidade sem uma mega-empresa por trás – bem, eu já “sabia” disso, mas uma coisa é saber, outra é experimentar pessoalmente. Esse livro conta de forma leve a história do movimento open-source, e é divertido ler os emails que o autor reproduz no livro, coisas do tipo “olá, meu nome é Linus Torvalds e tô começando um kernel UNIX pra me divertir e aprender nessas férias…”. Será que um dia isso vai estar em livros de história? Esse livro conta não só a história do Linux mas também do GNU, Apache, Perl, e vários outros projetos que são a espinha dorsal da Internet, é o sangue que circula no corpo criado pelos personagens dos dois livros anteriores. Bom pra ler num feriadão na praia, por ser um livro relativamente pequeno e rápido, mas que fornece um grande entendimento da motivação por trás dessa idéia aparentemente maluca de dar de graça o produto de centenas de horas de trabalho.

Accidental Empires: How the Boys of Silicon Valley Make Their Millions, Battle Foreign Competition, and Still Can’t Get a Date
Esse livro continua onde os dois primeiros terminam, o final dos anos 70, quando garotos no Vale do Sicílio começaram a se reunir em “computer clubs” pra construir versões caseiras de computadores, numa época que o computador mais barato que podia se comprar numa loja custava pelo menos 100.000 dólares. O livro é bem divertido, muitas vezes hilário, por tratar (como afirma o título) de gênios de 15, 20 anos, que, se por um lado falavam em revolucionar o mundo, por outro ainda tinham que resolver questões sobre como pegar o carro emprestado do pai pra ir na cidade ao lado comprar o último chip lançado pela Intel, ou como soar mais velho no telefone pra convencer um chefão da IBM a aceitar uma reunião. O legal aqui é que se você tem mais de 25 anos, vai lembrar de algumas coisas descritas aqui nesse livro, tal como o lançamento do Atari e do Apple II.

E você, quais livros sobre esses temas recomenda?


Escrito por rodbv

15/03/2009 em 14:34

Publicado em livros, Programação

Primeiros passos com Fluent NHibernate

com 3 comentários

Acredito que não sou o único que ficou “mal-acostumado”; com a facilidade trazida pela tecnologia Linq to SQL (L2S) que a Microsoft lançou no ano passado.

Infelizmente, há poucos meses a Microsoft anunciou baixinho que não iria mais desenvolver o mesmo L2S, e recomendou que todos adotassem em seu lugar Entity Framework, que, se por um lado é uma plataforma mais robusta, por outro lado é mais complexa, e, assim como L2S, atraiu muitas críticas de puristas que consideram essa plataforma difícil de estender e testar via unit tests.

Isso fez com que muitas empresas, incluindo a que eu trabalho, começasse a ver quais alternativas existem para O/RM, e de fato existe um produto no mercado que é livre e que já provou seu valor: NHibernate. Algo me diz que 2009 vai ser o ano que NHibernate vai entrar de fato na caixa de ferramentas da maioria dos desenvolvedores intermediários e avançados, ainda mais com o recente desenvolvimento da biblioteca Linq2NHibernate (L2NH) e Fluent NHibernate, esse último uma forma de configurar NHibernate usando código ao invés de arquivos XML.

Como nunca usei NHibernate nem L2NH, mas pretendo em muito breve, vou cobrir nesse artigo e nos próximos os primeiros passos com essa plataforma. Meu plano é usar esse código como base para um aplicativo ASP.NET MVC que também quero blogar aqui em forma de tutorial.

Pra dar uma visão melhor de onde quero chegar, o aplicativo que pretendo desenvolver é uma “Estante de livros virtual”, que me permita cadastrar livros (titulo, autor, imagem de capa etc) manualmente ou através de procura na Amazon, bem como marcar quais livros eu pretendo comprar, estou atualmente lendo e emprestei para alguém. Numa segunda fase esse aplicativo deverá suportar diferentes usuários. Ainda que eu não pretenta seguir práticas estritamente TDD, vou procurar criar unit tests para a maior parte do meu código.

Criando o modelo

Antes de mais nada vou iniciar uma solução nova, Bookshelf, com dois projetos: Bookshelf.Model e Bookshelf.Tests. O primeiro vai conter classes do tipo POCO (Plain Old C# Object), que significa classes que não têm qualquer conhecimento sobre qual plataforma é usada para armazenamento. Métodos como book.Update() e book.Delete() não vão existir aqui. Isso é uma forma de seguir o princípio de responsabilidade única, que mencionei num post anterior.

Solution1

O modelo inicial é extremamente simples: uma única classe, Book (vou escrever o código em inglês, incluindo nome de classes e comentários, pois vou compartilhá-lo com alguns colegas de trabalho noruegueses).

namespace Bookshelf.Model
{
    public class Book
    {
        public int ID { get; private set; }
        public string ISBN { get; set; }
        public string Title { get; set; }
        public string Author { get; set; }
        public int ReleaseYear { get; set; }
    }
} 

O próximo passo é mapear esse modelo, ou seja, ensinar ao NHibernate a acessar essa classe e como a mesma deverá ser representada no banco de dados. O NHibernate pode tanto mapear bancos de dados já existentes quanto criar novos bancos de dados.

namespace Bookshelf.Model.Mapping
{
    public class BookMap : ClassMap
    {
        public BookMap() 
        {
            Id(x => x.ID);
            Map(x => x.ISBN).CanNotBeNull();
            Map(x => x.Title).CanNotBeNull();
            Map(x => x.Author);
            Map(x => x.ReleaseYear);
        }
    }
}

Como o nome diz, dá pra ver que a leitura desse código é bem fluente, e todos os métodos são encadeáveis, podem ser colocados uns após os outros formando “frases”; ou uma DSL rudimentar. O único encadeamento que fiz por enquanto foi ao usar CanNotBeNull() para os campos que não quero que sejam nulos. Para que esse código compilasse, tive que adicionar duas referências: NHibernate.dll e FluentNHibernate.dll (todas DLLs necessárias estão incluídas no código-fonte, no final do artigo).

Testando o mapeamento

Agora já podemos testar esse mapeamento, pra ver se funciona mesmo. No meu projeto Bookshelf.Tests vou criar uma classe BookTests. Também vou precisar de uma classe FixtureBase que vai ser a base de todas minhas classes para testes, vai conter o código a ser reutilizado.

Vou começar por adicionar algumas referências: nunit.framework.dll, NHibernate.dll, FluentNHibernate.dll e FluentNHibernate.framework.dll. Nesse caso vou usar NUnit, mas qualquer outra biblioteca de testes poderia ser usada.


namespace Bookshelf.UnitTests
{
    public class FixtureBase where TModel : PersistenceModel, new()
    {
		protected SessionSource SessionSource { get; set; }
		protected ISession Session { get; private set; }

		[SetUp]
		public void SetupSession()
		{
			var cfg = new SQLiteConfiguration()
			                           .InMemory()
			                           .ShowSql();
			SessionSource = new SessionSource(cfg.ToProperties(), new TModel());
			Session = SessionSource.CreateSession();
			SessionSource.BuildSchema(Session);

			Session.Flush();
			Session.Clear();
		}

		[TearDown]
		public void TearDown()
		{
			Session.Close();
			Session.Dispose();
		}
	}
}

O mais interessante aqui é o método SetupSession(), no qual criamos um banco de dados SQLite que vai rodar na memória apenas. Isso significa que os testes serão extremamente rápidos. Ao usarmos SQLite nos testes não significa que nosso programa propriamente dito terá que usar SQLite também, se quisermos usar SQL Server ou Oracle basta usarmos uma configuração diferente, seja via XML, seja usando a sintaxe fluente parecida com a acima. O método ShowSql() fará que toda vez que um comando ao banco de dados seja executado, o SQL será mostrado no console.

Session é equivalente ao DataContext no Linq2SQL, é o objeto usado para disparar comandos no NHibernate.

Vamos agora dar uma olhada na nossa classe de teste:

namespace Bookshelf.UnitTests
{

public class BookTestModel : PersistenceModel
{
    public TestModel()
    {
        addMappingsFromAssembly(typeof(BookMap).Assembly);
    }
}

[TestFixture]
public class BookTests : FixtureBase
{
    [Test]
    public void Can_Insert_Books() 
    {
		//Arrange
		var book = new Book
		{
			ISBN = "123456",
			Author = "Isaac Asimov",
			Title = "Foundation Trilogy",
			ReleaseYear = 1950
		};

		//Act
		Session.Save(book);
		Session.Flush();

		//Assert
		Session.Clear();
		var dbBook = Session.Get(book.ID);
			Assert.AreNotSame(book, dbBook);
			Assert.AreEqual(book.ID, dbBook.ID);
			Assert.AreEqual(book.Title, dbBook.Title);
			Assert.AreEqual(book.ISBN, dbBook.ISBN);
			Assert.AreEqual(book.ReleaseYear, dbBook.ReleaseYear);
		}
	}
}

Na verdade temos duas classes aqui: a primeira, BookTestModel, serve apenas para aplicar o mapeamento que definimos em BookMap. Como a classe BookTests deriva de FixtureBase, o código de setup de FixtureBase será executado para configurar nosso banco de dados de acordo com o mapeamento de BookTestModel.

Finalmente, o teste insere um livro no banco de dados (lembrando que nesse caso é um banco de dados na memória, mas poderia muito bem ser um físico), e depois busca o livro do banco de dados e compara as propriedades. Assert.AreNotSame garante que não estamos nos referenciando ao mesmo livro na memória.

Hora de executar o teste!

TestResult

Perfeito! Repare que os comandos SQL também foram impressos.

Se quiser testar o código você mesmo, baixe aqui.

Num próximo artigo, vamos começar a usar Linq2NHibernate para criar a aplicação MVC.

Escrito por rodbv

18/01/2009 em 22:31

Publicado em NHibernate, Programação

Etiquetado com

Em busca de uma arquitetura sólida – Parte 2

com 3 comentários

Olá,

Na segunda parte de nossa série sobre princípios S.O.L.I.D., vamos analisar a segunda letra do acrônimo, O. Caso você não tenha lido a introdução a essa série, recomendo que dê uma passadinha lá antes de prosseguir.

Como vimos lá, a letra O representa o princípio Open-Closed, ou seja, Aberto-Fechado. O enunciado desse princípio é simples:

Uma classe deve ser aberta para extensão, e fechada para mudanças.

Ora, todo bom programador não busca escrever código que seja fácil de ser mantido, ou seja, fácil de mudar? E como o sistema pode mudar se minhas classes estão fechadas para mudança?

Mudanças são inevitáveis, a questão é minimizar riscos e facilitar testes. A idéia é reduzir ao máximo o número de mudanças que temos que fazer em classes já existentes (de preferência, a zero), e ao invés disso implementar mudanças através de novas classes que podem ser “plugadas” ao sistema, como blocos em um Lego.

Vamos ver como isso funciona com um exemplo prático. Suponha que você está encarregado de desenvolver um sistema que suporte busca de usuários por email. Uma forma de implementar isso seria

    public class Procura
    {
        public IEnumerable<Usuario> ProcuraUsuarioPorEmail(string email)
        {
            //efetua a procura no banco de dados
        }
    }

E seu programa principal seria mais ou menos assim:

            Procura p = new Procura();
            var resultado = p.ProcuraUsuarioPorEmail("usuario@meusistema.com.br");

Bem, nada mal. O código funciona, e o cliente está satisfeito. Pelo menos até a próxima semana, que ele te pede que implemente agora uma busca por nome.

Sem problemas, você abre sua classe e faz algumas alterações:

    public class Procura
    {
        public IEnumerable<Usuario> ProcuraUsuarioPorEmail(string email)
        {
            //efetua a procura no banco de dados
        }

        public IEnumerable<Usuario> ProcuraUsuarioPorNome(string nome)
        {
            //efetua a procura no banco de dados
        }
    }

Essa solução, a princípio, não é de todo ruim. Funciona, e seu código ainda é legível. A questão é, à medida que tais mudanças forem se acumulando com o passar dos meses e anos, quão complexa será sua classe?

Todos nós conhecemos a dor de abrir uma classe pra manutenção rápida e deparar com 200 linhas de código, muitas vezes se uma ordem lógica (ninguém tem tempo pra ficar colocando métodos similares em blocos), o que sempre nos deixa com medo que nossa mexida rápida cause problemas em algum lugar que nem sabíamos que existia.

Como poderíamos então fechar nossa classe pra mudança, mas mesmo assim permitir que o sistema evolua? A chave é extensão, e existem várias estratégias para tal.
Vamos olhar uma dessas possibilidades. Uma idéia boa de seguir quando desenhamos classes é separar o que é constante do que varia. No nosso caso, o que é constante é a necessidade de buscar usuários. O que varia é a estratégia usada para tal busca.

Podemos então definir uma interface para busca genérica, e uma classe que use tal interface, que representam as partes constantes da equação:


    public interface IEstrategiaDeProcura
    {
        IEnumerable<Usuario> EfetuaProcura(string criterio);
    }

    public class Procura
    {
        private IEstrategiaDeProcura _estrategia;

        public Procura(IEstrategiaDeProcura estrategia)
        {
            _estrategia = estrategia;
        }

        public IEnumerable<Usuario> EfetuaProcura(string criterio)
        {
            return _estrategia.EfetuaProcura(criterio);
        }

    }

Embora tal estrutura pareça mais complicada a princípio, a grande vantagem é que essas duas classes não terão mais que mudar tão cedo, podemos agora plugar quantas estratégias diferentes quisermos. O que fizemos na classe de procura é injetar a estratégia que ela deverá usar, ainda que ela não saiba nem tenha que saber qual estratégia concreta seja. Com isso estamos atendendo a outro princípio de boa pragramação OO que é programar em cima de abstrações, não classes concretas. Veja que ao reescrevermos essa classe também atingimos o princípio de responsabilidade única! Antes nossa classe tinha dois motivos para mudar (caso a procura por nome ou email mudassem, respectivamente), agora ela tem apenas um motivo para mudar, por tem apenas um simples método genérico. Como dizem os capixabas, matamos dois coelhos com uma caixa d’água só! :)

A busca por email e nome então implementariam tal interface, e serão usadas pela classe de procura:


    public class ProcuraPorEmail : IEstrategiaDeProcura
    {
        public IEnumerable<Usuario> EfetuaProcura(string criterio)
        {
            //efetua a busca por email
        }
    }

    public class ProcuraPorNome : IEstrategiaDeProcura
    {
        public IEnumerable<Usuario> EfetuaProcura(string criterio)
        {
            //efetua a busca por nome
        }
    }

E tais implementações seriam usadas assim:


            var p = new Procura(new ProcuraPorEmail());
            var resultado = p.EfetuaProcura("usuario@meusistema.com.br");

De agora em diante, toda vez que o cliente pedir um novo tipo de procura, basta implementar uma nova classe para a interface IEstrategiaDeProcura, escrever alguns unit tests para a nova classe, e pronto. Reduzimos em muito a chance de quebrar algo que antes funcionava, que é o grande problema em manutenção de sistemas.

Claro, esse exemplo é bem simples (por exemplo, não trata de procuras mais complexas), mas quis manter o exemplo simples para que o princípio Aberto-Fechado ficasse claro, sem distrações.

Seguindo esse princípio você estará abrindo possibilidades para que seu sistema seja extensível, por exemplo para que outras pessoas escrevam módulos de procura que você não implementou, mesmo que eles não tenham acesso ao código-fonte.

Até a próxima!

Escrito por rodbv

15/01/2009 em 02:57

Publicado em Programação, SOLID

Seguir

Obtenha todo post novo entregue na sua caixa de entrada.