Blog do Urubatan
msgbartop
Desenvolvedor, Palestrante, Escritor, Nerd Assumido e Pai do Marcus :D
msgbarbottom

19 Aug 10 Slides, videos e código da minha palestra no FISL 2010

Bom, este post esta bastante atrasado, mas me lembraram hoje pelo twitter que eu não publiquei por aqui o material da minha palestra do FISL deste ano.
O título da palestra era:
Transformando os pepinos do cliente no código de testes da sua aplicação com OSS
Quase a mesma apresentação que fiz no Agile Brazil 2010, mesmo assunto, exemplos parecidos, mas melhorei um pouco a forma de apresentar, e em vez de tentar escrever código na hora eu utilizei videos para os exemplos :D
Quem quiser dar uma olhada nos slides, coloquei eles no SlideShare.

E os videos coloquei no Vimeo, o com exemplo Java aqui, e o com exemplo Rails aqui.

FISL 2010 – Rails Cucumber BDD Sample creation from Rodrigo Urubatan on Vimeo.

Rails + BDD sample created as a sample to my presentation at Forum Internacional de Software Livre 2010.

FISL 2010 – Java Cucumber BDD Sample creation from Rodrigo Urubatan on Vimeo.

Java + BDD sample created as a sample to my presentation at Forum Internacional de Software Livre 2010.

Bom, espero que seja útil, se tiverem dúvidas é só deixar um comentário …

PS.: não tenho o código comigo agora, assim que possível subo pro github e coloco o link para o código aqui :D

Tags: , , , , , ,

05 Jul 10 Algoritmos básicos são importantes, você consegue melhorar este?

Bom, este post é mais minha opinião do que qualquer outra coisa, mas o que motivou a escrita dele foi este post no dzone: Will the real programmers please stand up? automated edition, onde o autor diz que apenas 1 em 20 entrevistados por ele conseguiam resolver de forma satisfatória testes simples como:

Escreva em C um método que inverta uma lista encadeada.

Claro que algumas restrições eram aplicadas, a assinatura do método não deveria ser alterada e a estrutura que armazenaria a lista encadeada também não deveria ser alterada. Elas eram apresentadas como o código abaixo:

1
2
3
4
5
6
typedef struct nodetype {
  int value;
  struct nodetype *next;
} node;
 
node* reverselist(node*)

Tentando automatizar um pouco o processo, ele tentou apresentar o código pronto com um bug, para que o bug fosse encontrado, mas o candidato sempre poderia escolher reescrever o código do zero se seguisse as regras acima.
Bom, olhei o código e achei que a recursividade utilizada estava só piorando o algoritmo, ele precisa de duas iterações para inverter a lista, então eu pensei, por que não escrever uma versão sem recursividade e que faça o processo todo em apenas uma iteração?
Bom, a minha resposta para o problema foi esta que esta abaixo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
	static class LinkedItem {
		int value;
		LinkedItem next;
 
		public LinkedItem(int value) {
			super();
			this.value = value;
		}
	}
 
	private static LinkedItem reverseLinkedList(LinkedItem currentHead) {
		LinkedItem nextHead = null, previousHead = null;
		while (currentHead != null) {
			nextHead = currentHead;
			currentHead = nextHead.next;
			nextHead.next = previousHead;
			previousHead = nextHead;
		}
		return nextHead;
	}

Eu escrevi em java, mas não faz diferença pois para o algoritmo que escrevi a unica diferença seria a sintaxe.
Para testar isto escrevi um programa simples que inicializa uma lista encadeada com números de 1 a 10 e imprime a lista antes e depois desta ser invertida. O código esta abaixo, se alguem quiser fazer uma versão melhor deste algoritmo por favor poste a sua resposta nos comentários.

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package test;
 
public class Main {
	static class LinkedItem {
		int value;
		LinkedItem next;
 
		public LinkedItem(int value) {
			super();
			this.value = value;
		}
	}
 
	private static LinkedItem reverseLinkedList(LinkedItem currentHead) {
		LinkedItem nextHead = null, previousHead = null;
		while (currentHead != null) {
			nextHead = currentHead;
			currentHead = nextHead.next;
			nextHead.next = previousHead;
			previousHead = nextHead;
		}
		return nextHead;
	}
 
	public static void main(String[] args) {
		LinkedItem head = initializeSampleList();
		printLinkedList(head);
		head = reverseLinkedList(head);
		printLinkedList(head);
	}
 
	private static LinkedItem initializeSampleList() {
		LinkedItem it = new LinkedItem(1), head = it;
		for (int i = 2; i < 10; i++) {
			LinkedItem it2 = new LinkedItem(i);
			it.next = it2;
			it = it2;
		}
		return head;
	}
 
	private static void printLinkedList(LinkedItem it) {
		while (it != null) {
			System.out.printf("%d ", it.value);
			it = it.next;
		}
		System.out.println();
	}
}

Eu achei espetacular a idéia deles de criar um site para automatizar este tipo de smoke test para entrevista de emprego de programadores, se o cara não consegue resolver isto deveria estudar um pouco mais antes de querer trabalhar com programação mesmo.
Mas se a idéia for testar a habilidade com algoritmos acredito que o candidato deveria poder escolher em que linguagem implementar, caso contrario, em alguns casos a habilidade com a linguagem estaria sendo testada, e acho que estas duas coisas deveriam ser testadas em separado.

E vocês o que acham sobre isto? Sobre este tipo de testes?
Já fizeram algum teste parecido com este em alguma entrevista de emprego?
E se você é o entrevistador, gostou da idéia deste tipo de teste? Acha util ou desnecessário?
Utiliza alguma técnica alternativa?
Olha o código produzido pelo candidato em projetos open source?

Bom, acho que por enquanto é isto :D

PS.: me decepcionei com a minha demora para implementar este algoritmo simples, levei uns 10 minutos, quando comecei a escrever achei que seria algo natural e funcionaria de primeira. Isto deve ter sido causado pela facilidade que temos hoje, como um Collections.reverse do java ou um [].reverse do ruby por exemplo. Raramente temos que lidar diretamente com este tipo de algoritmo básico, mas saber implementa-los é extremamente importante, pelo menos na minha opinião :D

Tags: , , , , ,

25 Jun 10 Material da minha palestra do Agile Brazil 2010

Para quem não sabe, eu apresentei hoje mais cedo, no Agile Brazil 2010, a palestra “Transformando os pepinos do cliente no código de testes da aplicação com Cucumber”.
O conteúdo da palestra ficou muito bom na minha opinião, mas a forma como eu apresentei, eu, durante a palestra comecei a achar chato …
Bom, espero que quem assistiu a paletra tenha aproveitado, e vou melhorar a forma de apresentar isto para o FISL, mas como eu sempre faço, o material da paletra esta sendo disponibilizado por aqui!
Os slides eu coloquei no slideshare:

Os videos da execução do cucumber estão aqui para quem quiser pegar:
Aplicação ASP.NET MVC 2 e Aplicação Java

E para quem quiser pegar o código dos exemplos, os links estão aqui:
Aplicação ASP.NET, Aplicação Java e Aplicação Ruby on Rails

A idéia que eu queria passar era que o cucumber pode ser utilizado para testar diversos tipos de aplicação, e acho que isto ficou claro :D

Espero que este material seja útil, e quem quiser comentar, sugerir, criticar, xingar, …
Pode usar o campo de comentários aqui do blog para isto.

Tags: , , , , , , , ,

18 Jun 10 Brincadeirinha com Kanban Board Online

Depois de ter algumas idéias mirabolantes, que acabei deixando de lado, resolvi brincar um pouquinho com javascript e fazer um mini protótipo de  um “Kanban Board”, sem usar nenhuma imagem, onde as tarefas fossem parecidas com aqueles alertas tipo “buble”, bom, o protótipo ficou beem feinho, mas deu pra tirar uma febre do meu CSS.
Para a parte Javascript usei o Jquery, a idéia básica é o seguinte:
Tenho 3 divs que representam 3 estados que qualquer tarefa pode estar: Backlog, Work In Progress e Done
Estas tem que estar uma ao lado da outra.
Dentro destas, quero ter blocos representando cada tarefa, e preciso conseguir mover estas tarefas entre os estados, para isto o JQuery facilita bastante a nossa vida, adicionei o jquery ui, e tudo o que eu precisei fazer foi o seguinte:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/jquery-ui.min.js"></script>
<script>
$(document).ready(function(){
    $("#backlog,#wip,#done").sortable({
        connectWith: '.connectedSortable',
        receive: function(event, ui) { 
            /*alert($(ui.item).attr('data-id') + ' -> ' + $(this).attr('id'));*/
            //Do something with the recently dropped item here
        }
    }).disableSelection();
 
});
</script>

Onde #backlog, #wip e #done são os IDs das divs principais …
Para posicionar estas divs coloquei elas dentro de um container de ID bublecontainers, e usei o seguinte CSS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
            #bublecontainers {
                background-color: gray;
                width: 600px;
                margin: 0 0 0 0;
                padding: 0 0 0 0;
            }
            #backlog, #wip, #done {
                width: 190px;
                margin-left: 5px;
                height: 340px;
                display: inline-block;
                float:left;
                border: solid 1px;
            }

E agora a parte importante, para fazer as tarefas parecerem bolhas, usei a seguinte estrutura HTML:

1
2
3
4
5
6
<div class="buble">
  <div class="buble-content">
Texto da task aqui
  </div>
  <div class="buble-arrow"></div>
</div>

E me aproveitei de como as bordas do HTML são montadas via CSS, para transformar isto em uma bolha, só precisei do CSS abaixo:

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
29
30
31
32
33
.buble-content {
                width: 100%;
                top: 0;
                margin: 0 0 0 0;
                padding: 8px 8px 8px 8px;
                text-align:justify;
                overflow: hidden;
                border-radius:10px;
                -moz-border-radius:10px;
                -webkit-border-radius:10px;
                background-color:#FFF;
                border-style: outset;
                text-indent: 10px;
 
 
            }
            .buble
            {
                display:inline-block;
                width:90%;
            }
 
            .buble-arrow
            {
                border-color:#FFF transparent transparent transparent;
                border-style:solid;
                border-width:17px;
                margin: -4 0 0 0;
                height:0;
                width:0;
                position: relative;
                left: 30%;
            }

As bordas arredondadas só vão aparecer em browsers mais modernos que tenham algum suporte a CSS3 rounded corners.

O experimento até que foi divertido, mas o código tem diversas falhas ainda, serviu para brincar um pouco :D

E o resultado vocês podem ver nesta coisa horrenda aqui de baixo :D (deem uma folga, fiz isto em 20 minutos ais ou menos só pra brincar e ver se a minha idéia era viável :D )

Back Log
Work In Progress
Done
Create a virtual Kanban Board

Make this VKB usable

write a blog post about it

make it using no images, just CSS

Se quiserem o código completo, segue abaixo:

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
<html>
    <head>
        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
        <script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/jquery-ui.min.js"></script>
        <style>
            #bublecontainers {
                background-color: gray;
                width: 600px;
                margin: 0 0 0 0;
                padding: 0 0 0 0;
            }
            .buble-content {
                width: 100%;
                top: 0;
                margin: 0 0 0 0;
                padding: 8px 8px 8px 8px;
                text-align:justify;
                overflow: hidden;
                border-radius:10px;
                -moz-border-radius:10px;
                -webkit-border-radius:10px;
                background-color:#FFF;
                border-style: outset;
                text-indent: 10px;
 
 
            }
            .buble
            {
                display:inline-block;
                width:90%;
            }
 
            .buble-arrow
            {
                border-color:#FFF transparent transparent transparent;
                border-style:solid;
                border-width:17px;
                margin: -4 0 0 0;
                height:0;
                width:0;
                position: relative;
                left: 30%;
            }
            #backlog, #wip, #done {
                width: 190px;
                margin-left: 5px;
                height: 340px;
                display: inline-block;
                float:left;
                border: solid 1px;
            }
            #bltitle, #wiptitle, #donetitle {
                height: 15px;
                text-align: center;
                width: 190px;
                display: inline-block;
                font-size: 20;
                font-height: bold;
            }
        </style>
        <script>
            $(document).ready(function(){
                $("#backlog,#wip,#done").sortable({
                    connectWith: '.connectedSortable',
                    receive: function(event, ui) { 
                        /*alert($(ui.item).attr('data-id') + ' -> ' + $(this).attr('id'));*/
                        //Do something with the recently dropped item here
                    }
                }).disableSelection();
 
            });
        </script>
    </head>
    <body>
        <div id="bublecontainers">
            <div id="bltitle">Back Log</div>
            <div id="wiptitle">Work In Progress</div>
            <div id="donetitle">Done</div>
            <div id="backlog" class="connectedSortable">
                <div class="buble" data-id="buble1">
                    <div class="buble-content">
                        Create a virtual Kanban Board
                    </div>
                    <div class="buble-arrow"></div>
                </div>
                <div class="buble" data-id="buble2">
                    <div class="buble-content">
                        Make this VKB usable
                    </div>
                    <div class="buble-arrow"></div>
                </div>
                <div class="buble" data-id="buble2">
                    <div class="buble-content">
                        write a blog post about it
                    </div>
                    <div class="buble-arrow"></div>
                </div>
                <div class="buble" data-id="buble2">
                    <div class="buble-content">
                        make it using no images, just CSS
                    </div>
                    <div class="buble-arrow"></div>
                </div>
            </div>
            <div id="wip" class="connectedSortable">
            </div>
            <div id="done" class="connectedSortable">
            </div>
            <div style="clear:both"></div>
        </div>
    </body>
</html>

Tags: , ,

18 May 10 HTTP e URL – Dois ilustres pouco conhecidos

Este post foi extraido de uma apostila de um curso que ministrei em 2007, eu queria passar este texto para um colega novo do trabalho e não tinha o texto disponível, então resolvi publicar isto aqui no blog que vai ser útil para mim e pode ajudar mais alguem também :D


Para que seja possível acessar qualquer página na internet, alem de outros recursos, como FTP, SSH e diversos outros serviços, foi definido um padrão de endereçamento chamado URL (Uniform Resource Locator), que possui o seguinte formato:

PROTOCOLO://[USUARIO[:SENHA]@]ENDERECO_SERVIDOR/CAMINHO_NO_SERVIDOR?parametro=valor&outro_parametro=outro_valor#ancoraNaPagina

Por exemplo:

http://www.google.com/search?q=ruby+on+rails

onde:

  • http é o protocolo
  • www.google.com é o endereço do host
  • /search é o caminho dentro do servidor
  • q=ruby+on+rails é o parâmetro de nome “q” com o valor “ruby on rails”

Como uma URL não pode conter espaços os espaços são substituídos pelo sinal “+”, isto se chama codificar o parâmetro.

Como uma URL não aceita nenhum caractere com código ASCII maior que 127, qualquer caractere nesta categoria precisa ser codificado. Esta codificação consiste em substituir os caracteres por um “%” mais 2 dígitos numéricos representando o código ASCII em hexadecimal do caractere desejado, como na tabela a baixo:

Caractere Código Hexadecimal
$ 24
& 26
+ 2B
, 2C
/ 2F
: 3A
; 3B
= 3D
? 3F
@ 40
Espaço 20
22
< 3C
> 3E
# 23
% 25
{ 7B
} 7D
| 7C
\ 5C
^ 5E
~ 7E
[ 5B
] 5D
` 60

Alguns destes caracteres são aceitáveis em uma URL, mas em algumas situações precisam ser codificados, como por exemplo o # que pode ser utilizado no final de uma URL para indicar ao browser qual parte da página mostrar, quando não for utilizado para este fim, deve ser codificado para evitar confusões.

Cabeçalhos HTTP

Cabeçalhos HTTP são uma forma de passar parâmetros extras, além da URL do browser para o servidor. Normalmente, estes parâmetros são utilizados para informar ao servidor as capacidades do browser e preferências do usuário, mas podem também ser utilizados para trocar dados adicionais além dos informados pelo usuário (que podem ser passados pela URL).

Os cabeçalhos HTTP são enviados com o seguinte formato, logo depois da URL e antes da primeira linha em branco na requisição:

nome: valor

O valor dos cabeçalhos deve ser codificados utilizando a mesma tabela de conversão das URLs.

Exemplo de Requisião HTTP

O usuário digita no browser a URL: http://www.google.com/search?q=ruby+on+rails

O browser abre uma conexão TCP/IP na porta 80 (padrão) do servidor www.google.com e envia o seguinte texto:

GET http://www.google.com/search?q=ruby+rails HTTP/1.0
User-Agent: Mozilla/4.73 [en] (X11; U; Linux 2.0.35  i686)
<Linha em branco no final>

O Servidor Responde:

HTTP/1.0 200 OK
Cache-Control: private
Content-Type: text/html; charset=ISO-8859-1
Set-Cookie: PREF=ID=059bd3c506c56f82:TM=1189888548:LM=1189888548:S=hV0hCZi26b1SuRY7; expires=Mon, 14-Sep-2009 20:35:48 GMT; path=/; domain=.google.com
Server: gws
Date: Sat, 15 Sep 2007 20:35:48 GMT
Connection: Close

<html><head>
<Resto do HTML da página>

Explicando o que foi enviado:

  • GET http://www.google.com/search?q=ruby+rails HTTP/1.0
    • GET é o método sendo utilizado
    • http://www.google.com/search?q=ruby+rails é a URL solicitada no servidor
    • HTTP/1.0 é a versão do protocolo sendo utilizada
  • User-Agent: Mozilla/4.73 [en] (X11; U; Linux 2.0.35  i686)
    • User-Agent é um cabeçalho enviado que informa ao servidor qual browser esta fazendo a requisição
  • Linha em branco marcando o final da transmissão.

Explicando a resposta:

  • HTTP/1.0 200 OK
    • HTTP/1.0 é a versão do protocolo
    • 200 é o código da resposta que informa que o processamento esta OK
    • OK é um texto livre escrevendo o status, cada servidor pode responder com uma descrição diferente relativa ao código
  • Nas linhas seguintes, antes do espaço, tem diversos HEADERS que podem ser utilizados pelo browser
  • Após a linha em branco, há o conteúdo da URL solicitada

Métodos HTTP

GET

O método GET é utilizado para buscar o conteúdo de uma URL e o conteúdo retornado para a mesma URL deve ser sempre o mesmo. Isso facilita que o browser possa guardar páginas em cache por algum tempo para melhorar a experiência do usuário.

POST

O método POST é utilizado para enviar dados para o servidor. Esse método não possui a obrigatoriedade de ter sempre o mesmo resultado para os mesmos dados enviados.

PUT

O método PUT é utilizado para criar recursos no servidor. Ele possui a característica do método GET, de ter a mesma resposta para o mesmo conteúdo enviado.

DELETE

Este método é utilizado para remover recursos do servidor.

HEAD

O método HEAD serve para solicitar ao servidor apenas o cabeçalho do conteúdo com os headers, para que o cliente decida se precisa requisitar o conteúdo completo ou não.

Os métodos mais utilizados pela maioria das aplicações são o GET para buscar dados e o POST para enviar dados. Os métodos PUT e DELETE raramente são suportados por uma aplicação, mas são bastante úteis quando se deseja implementar Web Services seguindo o padrão REST. Alguns outros protocolos utilizam métodos adicionais e até definem alguns novos, como por exemplo o protocolo WebDav.

Códigos de resposta HTTP

Os códigos de resposta HTTP possuem 4 famílias básicas que serão apresentadas abaixo. Mais informações sobre estes códigos de resposta podem ser encontradas nesta URL: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

  • 1XX – Códigos de informação, muito pouco utilizados
  • 2XX – Sucesso: o código mais utilizado desta família é o 200 que significa OK
  • 3XX – Redirecionamento: os códigos mais utilizados desta família são: 301 redirecionamento permanente e 307 redirecionamento temporário
  • 4XX – Erro do cliente: o código mais conhecido desta família é o 404 que significa recurso não encontrado
  • 5XX – Erro do servidor: o código mais conhecido desta família é o 500 que significa erro interno do servidor.

Cabeçalhos comuns

Como o protocolo HTTP não mantém estado portanto, cada request é independente. Para solucionar este problema os cookies podem informar ao servidor quem é o cliente que está acessando o recurso.

Segue uma lista de alguns cabeçalhos bastante utilizados.

  • Location: utilizado junto com os códigos 301 e 307 informando o novo endereço do recurso
  • Content-Type: informa o tipo MIME do recurso retornado ou enviado ao servidor
  • Set-Cookie: grava um cookie no browser
  • Cookie: envia os cookies armazenados no servidor
  • Cache-Control: informa se o browser pode ou não armazenar o recurso em cache
  • Content-Length: informa o tamanho do conteúdo enviado ou da resposta
  • Last-Modified: informa ao cliente quando foi a ultima alteração do recurso solicitado
  • Accept: informa ao servidor quais são os tipos de recursos que o cliente sabe processar
  • Allow: informa ao cliente quais as ações disponíveis para um recurso especifico
  • Content-Encoding: informa ao cliente ou ao servidor qual a codificação que está sendo utilizada para o conteúdo enviado ou recebido
  • Host: informa ao servidor qual o nome do host utilizado no request. Bastante útil para possibilitar a configuração de hosts virtuais no mesmo endereço IP

Bom, acho que era isto, claro que isto não é tudo sobre formação de URLs e sobre o protocolo HTTP, mas já é um bom começo.

Tags: , , , ,

27 Apr 10 Os 5 segredos para um sistema altamente escalável

Ontem enquanto caminhava do trabalho até o carro, conversando com um colega sobre infra estrutura para um sistema que ele estava pensando em construir. Qual arquitetura seguir, qual linguagem utilizar, qual framework, se rails era o suficiente para o que ele queria ou não, se devia usar ruby ou java, se ruby escala ou não escala, consegui convencer ele de que a resposta para a pergunta inicial dele é “depende”, mas também lembrei de alguns segredos que se aplicam em todos os casos que consigo lembrar agora para se criar um sistema web altamente escalável,por conseqüência, também são segredos bastante importantes para colocar a sua aplicação na “nuvem” e também são muito importantes para você escrever os serviços que farão parte da sua arquitetura SOA[bb] ou seus serviços REST.
Ou seja, sempre mantenha estas cinco regras em mente se o sistema que você esta desenvolvendo pode crescer um dia.

Sim, eu sei que estou sendo um pouco categórico demais, e que nenhuma regra se aplica a tudo e que existem excessões para o que estou escrevendo.

Stateless

Se você quer um sistema ou um serviço escalável, com certeza você quer que todas as requisições para este serviço sejam stateless.
Mas por que?
Simplesmente por que caso em um futuro próximo você precise rodar a sua aplicação em um cluster, você não prende um cliente a um nó do cluster, cada requisição pode ir para o nó do cluster que estiver com a menór carga naquele momento, fazendo com que o tempo de resposta aquela requisição seja o menor possível, mantendo o nó do cluster que vai atender a esta requisição ocupado o menor tempo possível.

Request Response Time

Acho que já falei um pouco sobre isto, mas imagine a seguinte situação, o sistema que você acabou de publicar bombou e agora em vez de ter que atender a 5 clientes (Você, seu melhor amigo, sua mãe, seu pai, sua namorada), agora o sistema tem que atender a 1k clientes simultâneos, e você pensa: Beleza, só preciso fazer um clusterzinho aqui, coloco mais duas maquinas e tudo beleza.
O problema é que o seu sistema antes estava com um “RRT” médio de 30s ou seja, com uma maquina só, você conseguia responder a duas requisições por minuto (considerando apenas um thread por máquina para facilitar o exemplo), agora com as duas maquinas novas você consegue atender a maravilhosos 6 requests por minuto. Ou seja, você tem um baita fiasco nas mãos.
Por isto o Request Response Time, ou seja, o tempo que a sua aplicação leva para atender a cada requisição tem que ser o mais baixo possível.
Por exemplo, enquanto escrevia este post criei uma aplicação rails com um cadastro de usuários de brinquedo, gerado pelo scaffold e o RRT é de 15.6ms (alto mas a maquina esta bastante carregada agora) o que permite que com uma thread seja possível atender a 64 requisições por segundo. Em um outro sistema que trabalhei, algumas requisições por diversos motivos chegaram a demorar 20s para retornar, mas isto foi resolvido claro :D
Bom, acho que deu para entender o por que o RRT é tão importante. Para clarear mais a idéia, imagine a seguinte situação que você já deve ter visto diversas vezes: Uma loja grande, com fila unica e 4 caixas atendendo. Se cada um dos caixas for muito demorado, você vai passar horas na fila, e não é economicamente viável ter um caixa por usuário. então a solução viável para este problema é otimizar o trabalho dos caixas para que cada caixa seja mais rápido e consiga atender a um maior número de clientes por minuto.

Cache

Cache é uma forma de melhorar muito o RRT, imagine que a sua mega aplicação seja uma página de classificados, em que a página inicial, que todos os clientes acessam ao chegar no site. Tenha uma lista com os produtos mais baratos anunciados.
Esta página precisa de uma consulta no banco de dados para ser renderizada, mas se você pensar bem os dados serão sempre os mesmos até que alguem delete um dos produtos listados ali ou cadastre outro mais barato.
Então faz todo o sentido “guardar uma copia” da página prontinha, e só mostrar aquela cópia para o próximo que acessar a página, e simplesmente deletar esta cópia quando uma das duas situações que pode alterar a página acontecer, ai no próximo acesso a página é renderizada novamente e a copia atualizada é guardada, e novamente ficamos utilizando apenas aquela cópia até que algo aconteça que possa fazer os dados ficarem desatualizados.
Isto é chamado de “cache”, na verdade esta é uma forma bem simples de cache. Estude aos recursos de caching do framework que você esta utilizando, do servidor de aplicações, do sistema operacional, do banco de dados.
Mas não ative tudo ao mesmo tempo, o correto uso de cache vai melhorar muito a performance da sua aplicação, mas o uso incorreto pode causar muitos problemas.

Remote Data

Muito pouca coisa é pior para um cluster do que uma aplicação que guarda dados localmente.
Imagine só o seguinte, naquela aplicação de classificados, você resolveu que as imagens correspondentes a cada anuncio ficariam gravadas na própria maquina do servidor, barbadinha, facilita o upload.
Ai é que mora o perigo.
Quando você precisar colocar isto pra rodar em um cluster, o segundo nó do cluster quando atendendo uma requisição, vai acreditar que as imagens estão naquele nó também, mas ele nem existia quando o anuncio foi criado, o upload foi feito pra outra maquina. Isto vai fazer com que o usuário veja uma página com a imagem quebrada.
Ahh, e como resolver isto?
Bom, existem diversas formas, uma bem simples é criar um servidor de “recursos estáticos”, ou seja, tudo o que é estático no site é servido por um endereço diferente, e quando for feito o upload de uma imagem, ela deve ser salva neste outro servidor. Também é possível utilizar um serviço como o Amazon S3 que vai fazer exatamente a mesma coisa que o seu servidor de recursos estáticos, mas de fora :D
Outra coisa a lembrar é do banco de dados, ele também não deve ficar na mesma maquina que o servidor da aplicação, ou o endereço do banco (seja ele SQL, document driven, …) deve ser configurável. Isto por que em um cluster, o ideal é que todos os nós acessem o mesmo banco de dados, ou no mínimo que todos os bancos do cluster estejam sincronizados.

Proxy Reverso

Um próxi reverso no contexto deste post vai ser utilizado para mascarar algumas das técnicas de caching, para implementar algumas técnicas de caching e principalmente para esconder do usuário que você tem diversos servidores, ele vai ser o “front-end” da sua aplicação, a mágica do cloud só pode acontecer atrás do proxy, e ele também pode esconder do usuário o fato de você usar um “servidor de recursos estáticos” sendo ele interno criado por você mesmo ou você usando um S3 da vida, o proxy reverso pode mascarar o endereço real da aplicação.
Mas nunca esqueça de um detalhe importante. Este proxy mesmo sendo vital para uma arquitetura cloud, ou um cluster menorzinho, ele acaba de adicionar um SPoF (Single Point Of Failure) se o proxy morrer, a sua mega aplicação esta fora do ar.

Bom, acho que era isto, sempre tem alguem perguntando como fazer um sistema escalável, como fazer um sistema robusto, como fazer um sistema para rodar em cluster ou na nuvem.
Este post não é a resposta final para todos os problemas, este post não responde estas perguntas para todos os casos, mas para a maior parte dos casos que me lembro agora, as dicas deste post são pontos imprescindíveis para qualquer aplicação ou arquitetura que pretenda ter um mínimo de escalabilidade.
Espero que este post seja útil para alguem, e se não concordarem com alguma coisa é só deixar um comentário.

Tags: ,

08 Jan 10 Utilizando Rake para o Build de projetos Java!

A alguns dias atrás eu li este twitt do Martin Fowler: “you don’t want a build tool which automatically downloads unresolved dependencies before cleaning out yr build output: http://bit.ly/59Rl85“, li todo o post e ele fala de forma bastante prolixa de alguns dos motivos que me fazem não gostar do Maven.

Não me levem a mal, eu já tentei utilizar ele algumas vezes, mas eu não consigo gostar de uma ferramenta que acha que sabe mais do meu projeto do que eu mesmo (ou o cliente, ou os desenvolvedores, …).
Ou pior que isto, uma ferramenta que tem a infeliz mania de tentar fazer um backup da internet antes de cada build só para verificar se tem a última versão das dependências disponível …

Como é citado no post, não acho que alguma ferramenta vá saber exatamente o que é necessário para qualquer projeto, até por que cada projeto é um projeto, e cada projeto tem suas peculiaridades, e eu simplesmente desisti todas as vezes que precisei configurar alguma destas peculiaridades no maven e voltei para o ANT.

O ANT é uma ferramenta bastante flexível, e pelo que eu tenho visto no mercado, fora alguns teimosos que preferem usar o maven mesmo passando muito mais trabalho do que o necessário, o ANT é o “defacto standard” para builds em Java, mas algumas vezes a “linguagem de script” do ANT dificulta as coisas quando se precisa realmente de um script para fazer alguma coisa durante o build, então resolvi usar Ruby para escrever os builds, ou seja, utilizar uma linguagem de scripts de verdade.

Ai pensei, como é que vou fazer para compilar meu projeto java utilizando o Rake? A linguagem de script é muito fodastica, é Ruby, eu me sinto bem programando em Ruby, mas e como compilar?

Fui perguntar ao oraculo e descobri o BuildR e o Raven que fora o fato de não utilizarem XML e sim Ruby, conseguem repetir todos os erros do Maven, eles parecem “ports do Maven para o Rake” e eu não sei por que alguem iria fazer isto, se você gosta tanto assim do Maven, use ele mesmo …

Mas do Rake eu gosto, me acostumei com ele trabalhando com o Rails, é muito fácil de automatizar tarefas relacionadas a um projeto utilizando o Rake, e não apenas o “build”, mas algumas tarefas que as vezes precisam ser automatizadas, como um merge freqüente com algum sub projeto desenvolvido em outra parte do mundo …

Isto me criou apenas um problema, como compilar, empacotar, …

Ou seja, me faziam falta as tasks básicas do ANT que eu utilizo sempre. As outras tarefas são melhor executadas na minha opinião pelo próprio Rake ou até mesmo por um script em Ruby, mas estas tarefas básicas iriam fazer falta, e para resolver isto eu criei uma classe wrapper para os comandos do JDK, que pode ser estendida depois, não é algo 100% rake, mas eu achei que ficou legal assim, se alguem não concordar e tiver idéias para melhorar estou aceitando sugestões :D

O wraper para os comandos do JDK ficou assim:

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
class JavaUtil
  RAW_COMMANDS = %w{appletviewer apt extcheck idlj jar jarsigner java javac javadoc javah javap javaw javaws jconsole jdb jhat jinfo jmap jps jrunscript jstack jstat jstatd jvisualvm keytool kinit klist ktab native2ascii orbd pack200 packager policytool rmic rmid rmiregistry schemagen serialver servertool tnameserv unpack200 wsgen wsimport xjc}
 
  def initialize(jdk_home=nil)
    @commands = {}
    @jdk_home = jdk_home || ENV['java_home']
    @default_for_command = {}
    @global_default = {}
    init_commands
  end
 
  def method_missing(met,*args)
    if RAW_COMMANDS.include? met.to_s
      execute_command met, *args
    else
      super.method_missing met, *args
    end
  end
 
  def respond_to?(met)
    RAW_COMMANDS.include?(met.to_s) || super.respond_to?(met)
  end
 
  def default_parameter(param,value)
    @global_default[param] = value
  end
 
  def default_parameter_for(met,param,value)
    params = @default_for_command[met] || {}
    params[param] = value
    @default_for_command[met] = params
  end
 
  private
    def init_commands
      @jdk_bin = File.join @jdk_home , "bin"
      RAW_COMMANDS.each do |cmd|
        @commands[cmd.to_sym] = File.join @jdk_bin, cmd
      end
    end
 
    def update_or_concat_with_defaults(opts,defaults)
      defaults.each do |key,value|
        param = opts[key]
        if !param
          param = value
        else
          if param.is_a? Array
            param << value
            param.flatten!
          end
        end
        opts[key] = param
      end
    end
 
    def execute_command(cmd, *args)
      actual_command = @commands[cmd.to_sym]
      if args
        opts = {}
        opts.update args.pop if args.last.is_a? Hash
        update_or_concat_with_defaults opts, @global_default
        update_or_concat_with_defaults opts, @default_for_command[cmd.to_sym] if @default_for_command[cmd.to_sym]        
        opts.each do |key, value|
          param = value
          param = param.join File::PATH_SEPARATOR if param.is_a? Array
          actual_command << " -" << key.to_s << " "  << param
        end
        actual_command = "#{actual_command} #{args.join ' '} "
      end
      puts actual_command
      res = %x{#{actual_command}}
      puts res
      [$?,res]
    end
end

A minha idéia dos parâmetros default globais tem um pequeno problema, alguns comandos não recebem os mesmos parâmetros, mas é possível setar parâmetros padrão por comando, o que ficou legal, e deixou a compilação mais limpa …

A classe pode ser utilizada com qualquer JDK, inclusive instâncias diferentes podem utilizar JDKs diferentes para o mesmo build, basta passar o “JAVA_HOME” no construtor, por padrão a variável de ambiente é utilizada …

Mas beleza, como é que eu utilizo esta tranqueira em um Rakefile agora? bom, o meu Rakefile para o projeto de exemplo ficou assim:

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
29
30
31
32
33
34
35
36
37
38
39
40
41
require 'lib/java_util'
@java_util = JavaUtil.new
 
task :default => :test
 
SRC_FILES = FileList.new 'src/**/*.java'
TST_FILES = FileList.new 'test/**/*.java'
CLASSPATH = FileList.new "#{File.join(ENV['TOMCAT_DIR'], 'lib').gsub /\\/,'/'}/*.jar"
 
@java_util.default_parameter_for :java, :classpath, CLASSPATH.to_a
@java_util.default_parameter_for :javac, :classpath, CLASSPATH.to_a
 
directory 'output/classes'
directory 'output/tests'
 
desc "Compile all the java files"
task :compile => ['output/classes','output/tests']  do
  @java_util.javac SRC_FILES, :d => 'output/classes'
  @java_util.javac TST_FILES, :d => 'output/tests', :classpath => ['output/classes',"#{ENV['JUNIT_DIR']}\\junit-4.4.jar"]
end
 
desc "Creates the package after compilation"
task :package => :compile do
  @java_util.jar '-cf output/target.jar -C output/classes .'
  cp 'output/target.jar', 'WebContent/WEB-INF/lib'
  @java_util.jar '-cf output/target.war -C WebContent .'
end
 
desc "Runs the tests after packaging"
task :test => :package do
  test_classes = FileList.new 'output/tests/**/*.class'
  test_classes.gsub! /output\/tests\/(.*)\.class/,'\1'
  test_classes.gsub! /\//, '.'
  @java_util.java "org.junit.runner.JUnitCore #{test_classes.join ' '}", :classpath => ['output/tests','output/target.jar',"#{ENV['JUNIT_DIR']}\\junit-4.4.jar"]
end
 
desc "Clean up all the mess we created"
task :clean do
  rm_f 'output'
  rm_t 'WebContent/WEB-INF/lib/target.jar'
end

O código dos testes não precisava ser tão complexo, eu poderia ter criado um wrapper para ele, a mesma coisa para a criação do jar, poderia até mesmo ter utilizado o “rubyzip” para deixar mais bonitinho, mas a idéia por enquanto é ser bem simples.

Estou utilizando este build em um projeto, se engrenar provavelmente a biblioteca vá crescendo, mas acho que por agora já serve para começar a brincar e ver o que vocês acham da idéia.
A classe “JavaUtil” precisa ser mais testável, mas isto tornou ela complexa demais para o exemplo deste post, se eu convencer o resto da equipe a continuar usando esta solução vou melhorando ela aos poucos :D

Acho que vou separar a montagem do comando e a execução do mesmo, ou transformar cada comando em uma classe para facilitar a expansão da biblioteca e tornar mais testável, ou até mesmo as duas coisas.
No momento a classe não é nada testável, mas já esta divertida e o meu bluid diminuiu muitas linhas depois que eu converti ele de ANT para Rake utilizando esta lib :D

PS.: quem quiser pegar o projeto de testes para brincar, só para olhar ou até mesmo para implementar algumas melhorias, ele esta publicado no github. Se implementarem alguma melhoria, não esqueçam de enviar um pull request para que eu possa fazer o merge das alterações :D

Tags: , , , ,

30 Sep 09 Seja comunicativo, diga por que escreveu este código

Bom, no último post eu disse para você não ser repetitivo, e não escrever nos comentários o que o seu código faz. Se você precisa comentar o que o seu código faz, você realmente precisa refatorar o seu código e torna-lo mais legível
Mas comentários no código não são sempre ruins, o ruim é você escrever em português o que o seu código faz em java, mas existem diversas situações que veremos a seguir onde os comentários são ótimos, ou pelo menso são úteis …

Por exemplo, na interface pública de um serviço, um JavaDoc informando para que serve aquele serviço e com exemplos de utilização são sempre muito bem vindos, principalmente se este comentário informar quais são as validações que serão feitas nos parâmetros e quais são as restrições ao uso daquele serviço …
Eu não posso pegar exemplos de projetos em que estou trabalhando por que isto seria motivo para demissão por justa causa por quebra de sigilo :D
Então vamos ficar com alguns exemplos públicos e alguns exemplos mais simples que eu conseguir pensar na hora …

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * This is the only interface you need to create and manage system users, but if you need to manage user privileges take a look at the @link{PrivilegesService} interface
 */
public interface UsersService {
   /**
    * This method should be used only if you are already authenticated in the system, do no forget to send the session id header when calling methods that are not public like this one.
    * To create a user you need to provide all the needed information, this method will create  user in the database and then will try to create the user in the backend system, if the creation works the two accouns will be linked otherwise the database reccord will be erased. 
    * @param userName the user name must be unique within the LDAP domain, this means that there can
    * @param password
    * @param email
    */ 
   public User createUser(String userName, String password, String email);
}

Neste exemplo, não há necessidade alguma de colocar um comentário dizendo que o método createUser cria um usuário, mas o javadoc do exemplo informa de algumas pré condições para a execução do método e também diz qual o fluxo de execução e possíveis falhas. Como neste exemplo o UsersService é a interface publica de um serviço, esta documentação é bastante bem vinda, pois vai ajudar os usuários que vão acessar o serviço a saber o que fazer antes de criar um usuário e a saber possíveis motivos para as falhas.

Bom, acho que todos os outros exemplos que eu procurar vão ser parecidos com isto, resumindo este post e o anterior:
Se um comentário diz o que o método faz, o código é um lixo ou o comentário é redundante, o que o método faz tem que ser óbvio pelo nome do método
Se o comentário diz quando o método deve ser utilizado, algumas pré condições, o fluxo de execução, possíveis erros, e principalmente se ele esta em uma interface publica do sistema, que vai ser entregue a outra equipe/programador/empresa. o comentário é útil
Se o comentário esta em algum método ou variável privada, provavelmente tem algum problema por ai.

Acho que com isto eu encerro este assunto de comentários, eu posso ter me expressado errado, ou o mais provável, muitas pessoas leram o título do post e saíram berrando antes de ler o resto, mas a idéia básica é, o seu código tem que ser muito fácil de ler, os nomes das classes, métodos, variáveis, pacotes e quaisquer arquivos envolvidos no sistema devem dizer o que são, para que servem e por que estão ali. Se você esta colocando um comentário que diz que o método “create” cria um usuário, você deveria chamar o método de “createUser”, mas se o comentário esta informando o usuário do método de que o nome de usuário utilizado não precisa ser único mas o email sim, pois é este o campo utilizado como chave, ai você tem um comentário útil (e um sistema extranho :D ).

Não é por que o seu código tem comentários que ele é um lixo, mas tome cuidado por que é muito fácil utilizar comentários como desculpa para código ruim. E neste caso, talvez você precise estudar um pouco de rafactoring.

Tags: , , , ,

24 Sep 09 Software Auto Identificável – Self Identifying Software

2identify_yourself_logo3333
esta imagem foi encontrada na web usando o images.google.com e eu achei que tinha a ver com o post

Eu não conhecia o conceito até ver este twit do CV falando deste post sobre Self Identifying Software. E lendo o post percebi que já passei e algumas vezes ainda passo pelo mesmo problema: Identificar qual versão do software esta instalada em um servidor, ou em que versão do sofware algum bug apareceu ou aconteceu ou acontece …

Bom, eu curti a idéia e fiquei pensando em como implementar isto, pelo menos em projetos Java, disto sairam estes “code snippets” abaixo …
Bom, normalmente trabalho com o ANT para fazer o build de projetos Java, e tenho utilizado o Subversion (sim, eu conheço o GIT e gosto dele, mas no momento não vai rolar no trampo, mas uso para projetos pessoais :D )
Então, fui a página do subversion e baixei o SVNANT, desenvolvido pelo pessoal do subclipse, e integrei ele no meu build assim:

1
2
3
4
5
6
7
8
9
10
        <path id="svn_tasks">
		<fileset dir="${directory_you_unzipped_the_svnant_package}" includes="svn*.jar">
		</fileset>
	</path>
	<taskdef classpathref="svn_tasks" resource="org/tigris/subversion/svnant/svnantlib.xml" />
	<target name="_setup_svn_info">
		<svn failonerror="false" javahl="true" svnkit="false">
			<info target="${basedir}" verbose="true"/>
		</svn>
	</target>

Depois disto, em qualquer parte do build em que você for criar um .jar, .war ou qualquer tipo de pacote java, basta fazer algo parecido com isto:

1
2
3
4
5
6
7
8
9
        <target name="build_jar" depends="_setup_svn_info,compile">
		<jar destfile="${dist.dir}/${jar.name}">
			<fileset dir="${basedir}/bin" includes="*.*" />
			<manifest>
				<attribute name="SVN-URL" value="${svn.info.url}" />
				<attribute name="SVN-REV" value="${svn.info.rev}" />
			</manifest>
		</jar>
        </target>

Claro que o importante é o depends e o manifest, o resto vai depender do seu build, isto não é nem um exemplo real, escrevi direto aqui no blog para dar a idéia, então se tiver algum problema com o código me avisem nos comentários :D

Mas isto não é útil se você não conseguir ler o MANIFEST.MF do .jar onde a sua classe se encontra, então estou colocando aqui também um exemplo de código para isto, mas lembre-se de alterar o nome da classe para cada pacote, caso contrário você nunca saberá de qual pacote a classe esta sendo carregada :D

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package blog.urubatan;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.jar.Manifest;
 
public class ExemploDoUrubatan {
	private Manifest manifest;
 
	private void initManifest() throws URISyntaxException,
			FileNotFoundException, IOException {
		Class<?> clazz = getClass();
		URL classContainer = clazz.getProtectionDomain().getCodeSource()
				.getLocation();
		File manifestContainer = new File(classContainer.toURI());
		File metaInf = new File(manifestContainer, "META-INF");
		File manifestFile = new File(metaInf, "MANIFEST.MF");
		manifest = new Manifest(new FileInputStream(manifestFile));
 
	}
 
	public ExemploDoUrubatan() throws URISyntaxException,
			FileNotFoundException, IOException {
		initManifest();
	}
 
	public String getSvnUrl() {
		return manifest.getMainAttributes().getValue("SVN-URL");
	}
 
	public String getSvnRevision() {
		return manifest.getMainAttributes().getValue("SVN-REV");
	}
 
	public static void main(String[] args) throws FileNotFoundException,
			URISyntaxException, IOException {
		ExemploDoUrubatan ex = new ExemploDoUrubatan();
		System.out.println(ex.getSvnUrl());
		System.out.println(ex.getSvnRevision());
	}
}

Se for a versão de um arquivo .war o código pode ser colocado em um servlet com uma URL conhecida, ou em um listener que vai guardar esta informação no servlet context para ser impresso depois por uma URL conhecida …
Se o servlet for a opção selecionada, o código ficaria parecido com isto:

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
29
30
31
32
33
34
35
36
37
38
39
package blog.urubatan;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
public class ServletExample extends HttpServlet {
	private static final long serialVersionUID = 1L;
 
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		super.doPost(req, resp);
	}
 
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		String warRoot = getServletContext().getRealPath(".");
		File manifestContainer = new File(warRoot);
		File metaInf = new File(manifestContainer, "META-INF");
		File manifestFile = new File(metaInf, "MANIFEST.MF");
		Manifest manifest = new Manifest(new FileInputStream(manifestFile));
		PrintWriter writer = resp.getWriter();
		Attributes mainAttributes = manifest.getMainAttributes();
		String svnUrl = mainAttributes.getValue("SVN-URL");
		String svnRev = mainAttributes.getValue("SVN-REV");
		writer.format("URL: %s\nRev:%s\n", svnUrl, svnRev);
	}
 
}

Com isto, pelo menos para projetos java, já cobrimos duas das situações mais comuns, que são saber a versão de uma API e saber a versão de uma aplicação WEB.
Com isto já é possível verificar o deploy de aplicações durante o build se o script for um pouco mais inteligente, o pessoal de testes tem condições de dizer exatamente qual foi a build que gerou o problema, é possível construir um “dashboard” com a versão de tudo que é utilizado no sistema, facilitando bastante a identificação de onde o problema ocorre, e principalmente, no caso de clusters, permitindo que seja verificada a versão em cada um dos nós de uma forma fácil …

Agora no caso do Rails, eu ainda não consegui decidir qual a melhor abordagem para isto …
criar um arquivo com estes meta dados dentro do diretório config, atualizar este arquivo por uma task rake toda vez que for executar um deploy via capistrano e criar um controller para informar a versão?
As gems já tem um mecanismo de versionamento, seria só atualizar a versão da gem a cada build, coisa que pode ser feita até com keywork expansion, ou utilizando o mesmo esquema do rake mencionado antes.
Bom, vou pensar mais nisto, derepente rola até criar um plugin para aplicações rails pra facilitar a vida :D
O que vocês acham?

Tags: , , , ,

16 Sep 09 Não seja repetitivo, nunca comente o que o seu código faz

Eu sei que já falei sobre comentários antes, mas acho que a abordagem não agradou muito, a maior parte das pessoas leu só o título do post e não prestou atenção no texto, então vamos tentar uma abordagem diferente.
Este post é o primeiro de uma série de 2, o próximo post vai dizer que é necessário comentar o código, mas com comentários decentes, então, sem gritaria por aqui por enquanto, ok?
Mas vamos ao que interessa …
Na minha opinião, qualquer programador Java, ou até mesmo, qualquer programador, que colocar os olhos no código abaixo, vai entender quase que instantaneamente o que ele faz, se alguem não concordar com isto, avise por favor …

1
2
3
4
5
6
new EmailMessage()
    .from("exemplo@urubatan.com.br")
    .to("gerente@empresa.com")
    .withSubject("Aprovação de processo")
    .withBody("Descrição bastante detalhada do que precisa ser aprovado")
    .send();

(sim, copiei o exemplo do blog do GC :D )
Então, eu acredito que se alguem quiser colocar um comentário neste código dizendo algo do tipo:

  • Envia email
  • Cria uma mensagem de email, configura propriedades e depois envia

Vai estar simplesmente sendo repetitivo, e poluindo o código com comentários inúteis, isto seria dizer o que o código faz, e se você comenta o que o seu código faz você é sim um perdedor que gosta de jogar trabalho no lixo!
Ahh, mas e se meu código não é assim tão claro? E se meu código é parecido com isto:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// create some properties and get the default Session
Properties props = new Properties();
props.put("mail.smtp.host", _smtpHost);
Session session = Session.getDefaultInstance(props, null);
 
// create a message
Address replyToList[] = { new InternetAddress(replyTo) };
Message newMessage = new MimeMessage(session);
if (_fromName != null)
    newMessage.setFrom(new InternetAddress(from,
        _fromName + " on behalf of " + replyTo));
else
    newMessage.setFrom(new InternetAddress(from));
    newMessage.setReplyTo(replyToList);
    newMessage.setRecipients(Message.RecipientType.BCC, _toList);
    newMessage.setSubject(subject);
    newMessage.setSentDate(sentDate);
 
// send newMessage
Transport transport = session.getTransport(SMTP_MAIL);
transport.connect(_smtpHost, _user, _password);
transport.sendMessage(newMessage, _toList);

Bom, se o seu código é assim, então você precisa estudar um pouco de refactoring :D
Ahh, mas estou alterando uma parte da aplicação com muito código legado …
Bom, você deveria utilizar pelo menos alguns “extract method” para facilitar um pouco a leitura, e não comentar o que o código faz.

Imaginem a seguinte cena:
Você esta caminhando na rua e entra em uma loja, no momento em que você entra na loja, vê uma placa escrito: Roupas masculinas
Dois passos adiante, um vendedor chega e diz: Senhor, aqui o senhor vai encontrar roupas masculinas
Mais alguns passos e outro vendedor: Vendemos roupas masculinas aqui senhor.
E mais outra placa dizendo: Aqui roupas masculinas

Se você ainda estiver na loja e não bater no próximo que lhe visar que ali são vendidas roupas masculinas, no mínimo acabou de ocorrer um desperdício absurdo de esforço para informar exatamente a mesma coisa.
E este caso do email não é um dos mais comuns, o motivo deste post é que em muitos lugares eu vejo código parecido com:

1
2
3
4
5
6
/**
 * Set's the active property
 */
public void setActive(boolean active) {
   this.active = active;
}

(Isto não é uma critica a ninguém especifico e a todos os que já escreveram código assim, não foi uma nem duas vezes que vi isto por ai …)
um método de nome “setActive” com o comentário “Set’s the active property” é no mínimo redundância, e desperdício de tempo.
Então, eu não estou dizendo, nunca comentem o seu código, mas estou dizendo, se você precisa dizer o que o seu código faz, o seu código tem problemas, mas comentários são úteis, principalmente em interfaces públicas se você estiver informando por que ou como o código faz o que faz …
Mas tenha como regra, é proibido um comentário dizendo o que o código faz, pois ele é um sinal de que o código esta muito ruim!!

Ahh, e só para terminar, a biblioteca que permite aquele código de envio de emails bonitinho é a “Fluent Mail API” e o nome desta “técnica” é “Fluent Interfaces”, uma “técnica” bastante utilizada por quem trabalha com “Domain Driven Design”, e DDD faz Orientação a Objetos realmente mais divertida e mais útil, vou falar mais sobre isto em breve :D .

Tags: , , , , ,

05 Jul 09 Eclipse 3.5 Galileo – Mais rápido, menor foot print e mais “cool” do que no ano passado!

Galileo is Here
É, novamente chegou aquela época do ano, em que o pessoal da Eclipse Foundation libera mais um “Release Train”, ou seja, uma nova versão de diversos projetos simultaneamente e compatível entre sí.
Isto é melhor ainda para quem lembra dos tempos pré Calisto, que foi o primeiro “Release Train”, naqueles tempos longínquos era necessário baixar cada um dos plugins na mão, e torcer para ter pego uma versão compatível, o que na maioria das vezes não era verdade …
Utilizar o eclipse, principalmente com o WTP era uma tarefa apenas para os mais fortes e mais preparados, e Darwin era quem ditava as regras da comunidade.
Existiam projetos paralelos de ambientes para desenvolver WEB com o eclipse que tentavam facilitar a vida dos menos preparados, mas estes não tinham vez quando se falava em qualquer outro projeto da Eclipse Foundation fora o JDT.
Mas estes tempos acabaram, os Release Trains possibilitam o acesso ao poder do eclipse para todos os interessados, e não apenas aos iniciados.
E este post cheio de firulas e histórias sem nexo foi escrito para falar um pouco mais do Release Train de 2009, o Galileo; que diferente de seus antecessores Callisto, Europa e Ganymede não é o nome de uma das luas de Jupiter, mas o nome do grupo de luas de Júpiter que inclui as 3 anteriormente citadas e também Io, e é também o nome do cientista que em 1609 oficialmente descobriu as 4 maiores luas deste planeta.
Mas alem de ser uma das luas de Júpiter, é também o nome do Release Train do Eclipse em 2009 que inclui os seguintes projetos:

Project Name Version Project Summary Download
Acceleo Acceleo 0.8.0 Download
Accessibility Tools Framework 0.7.0 Download
ATL – Atlas Transformation Language 3.0.0 Download
Buckminster Component Assembly Download
Business Intelligence and Reporting Tools (BIRT) Download
C/C++ Development Tooling (CDT) 6.0 Download
CDO Model Repository 2.0.0 Download
Dali Java Persistence Tools 2.2 Download
Data Tools Platform 1.7 (Galileo) Download
Dynamic Languages Toolkit 1.0 Download
Eclipse Communication Framework ECF 3.0 Download
Eclipse Modeling Framework (EMF) 2.5.0 Download
Eclipse Packaging Project 1.1.0 Download
Eclipse Platform 3.5 Download
Eclipse Project 3.5.0 Download
Eclipse Web Tools Platform Project WTP 3.1.0 (Galileo) Download
EclipseLink Project 1.1.2 Download
EMF Compare Download
EMF Teneo Model Relational Mapping 1.1.0
Equinox 3.5 Download
GEF – Graphical Editor Framework 3.5.0 Download
Graphical Modeling Framework 2.2.0 Download
Java Workflow Tooling JWT 0.6 Download
JDT – Java development tools Download
M2T JET (Java Emitter Templates) – aka JET2 M2T JET 1.0.0 (Galileo) Download
MDT OCL (Object Constraint Language) 1.3 (Galileo) Download
MDT UML2 Tools 0.9.0 (Galileo) Download
MDT XSD (XML Schema Definition) 2.5.0 Download
MDT-UML2 3.0.0 Download
Memory Analyzer 0.8.0 Download
Mobile Tools for Java Download
Model Development Tools (MDT) Galileo Download
Model To Text (M2T) Galileo (xpand 0.7, acceleo 0.8, jet 1.0) Download
Model-to-Model Transformation (M2M) Galileo Simultaneous Release Download
Monitoring Tools 4.6.0 Download
Mylyn 3.2 Download
Net4j Signalling Platform 2.0.0 Download
PHP Development Tools 2.1.0 Download
Rich Ajax Platform 1.2 Download
Riena Platform Project 1.1.0. Download
SCA Tools 2.0.0 Download
SOA Tools 2.0 Download
Source Editing 3.1.0 (Galileo) Download
Subversive – SVN Team Provider Download
Swordfish 0.9.0 Download
Target Management 3.1 Download
Test and Performance Tools Platform Project 4.5.3 Download
Testing Tools TPTP v4.6 Download
Textual Modeling Framework org.eclipse.xtext Download
Tools for mobile Linux 0.3 Download
TPTP Platform TPTP v4.6 Download
Tracing & Profiling Tools TPTP v4.6.0 Download

Bom, se você não dormiu até chegar aqui, vamos ao que interessa, o que tem de bom, e de diferente nesta versão do eclipse, fora um monte de números de versões novas.
os meus comentários são referentes ao Download “for J2EE Developers”, ou seja, com o WTP já instalado.

A primeira coisa que notei foi que esta versão do eclipse, não passou de 200M de memória em nenhum momento, tenho utilizado ele o dia inteiro, e a ocupação de memória fica em média entre 130M e 160M, bem melhor que o Ganymede que estava sempre entre 300M e 600M. Isto por sí só já é uma grande vantagem, o Eclipse esta bem menos pesado, e todas as operações estão com um tempo de resposta perceptível bem menor. Não sei se o tempo real esta menos, mas isto não me importa muito mesmo :D
Uma coisa que não gostei, é que aquela perspectiva podre “Java EE” é a perspectiva padrão, eu sempre prefiro utilizar a perspectiva Java como padrão.
O Suporte ao ANT continua fraco, se em um projeto existirem muitos arquivos build.xml, em algum momento o editor vai entrar em coma e só vai voltar a funcionar depois de reiniciar a IDE, mas o auto complete esta mais inteligente e mais rápido …
Uma coisa que achei muito legal é que o eclipse agora reconhece os XMLs gerados por um output do JUnit Report do ANT e abre ele na mesma view dos resultados do JUnit executados pela IDE, o que facilita muito a visualização :D
A versão nova do gerenciador de plugins também esta bem legal, ficou mais intuitivo para os novos usuários …
Mas o eclipse ainda não vem com suporte nativo ao subversion, o plugin esta no repositório do Galileo, mas não vem instalado, quando você instala o eclipse, só tem suporte a CVS o que é sofrível. E mesmo assim, só existe suporte “oficial” para estes dois SCMs, se quiser usar GIT vai ter que correr atrás.
Mas nem tudo são problemas, a nova view de “Problems” com as coisas agrupadas ficou bem legal.
Um recurso novo espetacular do editor, é a possibilidade de selecionar blocos, sempre senti falta disto no Eclipse :D
O Code completion do editor Java esta mais rápido, ou pelo menos parece mais rápido, e pode ser por que criei um workspace novo, mas parou de ocorrer um erro muito chato do Mylyn antes de apresentar os proposals para o code completion que me enchia o saco na versão anterior, mas acontecia só uma ou duas vezes por dia …
Outra coisa legal é que agora quando se segura o “Control” com o mouse sobre um método ou classe, antes sempre era aberta a implementação, agora o Eclipse pergunta se você quer ver a implementação ou a definição do método.
Achei muito extranho o icone novo do eclipse, principalmente por que o icone da aplicação não mudou, mas o icone no task bar do windows mudou, parecem duas aplicações diferentes :D
(E sim, antes que alguem comente, aqui no trampo sou obrigado a usar windows)

O suporte a Java ME ainda é bem mais fraco que o do NetBeans, mas o eclipse tem suporte a desenvolvimento em C++ para dispositivos móveis (não cheguei a testar) o NetBeans não tem …
O suporte a linguagens dinâmicas também melhorou, pelo menos o suporte a Ruby melhorou, mas ainda não existe suporte direto ao Rails …
O editor de C++ esta mais rápido, mas ainda com um code completion bem fraco e um suporte quase inexistente a refactorings, mas o “quase” já faz isto ser muito melhor do que no Visual Studio.
Ocorreram também diversas mudanças estruturais no Eclipse, mas como eu sou apenas mais um usuário da ferramenta, vou deixar este tipo de comentário para quem realmente entende.

Bom, se você teve paciência de ler até aqui é por que esta interessado no Eclipse (ou não tinha nada melhor para fazer :D ), então esta na hora de acessar o site do Eclipse e baixar o galileo.
Nesta página existem diversas opções, uma delas vai te deixar feliz, mas se você é um usuário “Hard Core” das antigas, e realmente gosta de passar trabalho, baixe o Eclipse Classic no final da página e monte o seu ambiente com os plugins que estiver com vontade :D
Se você não conseguir se decidir qual é a versão certa para você, basta acessar esta página, que diz o que esta incluído em cada um dos pacotes disponíveis para download.
Para facilitar a sua vida, copiei a tabela com os downloads e coloquei aqui :D


Tools for Java developers creating Java EE and Web applications, including a Java IDE, tools for Java EE, JPA, JSF, Mylyn and others. More…

Downloads: 202,591

Windows

Mac OS X (Carbon)

Mac OS X (Cocoa)
Linux 32bit
Linux 64bit

The essential tools for any Java developer, including a Java IDE, a CVS client, XML Editor and Mylyn. More…

Downloads: 74,402

Windows

Mac OS X (Carbon)

Mac OS X (Cocoa)
Linux 32bit
Linux 64bit

Tools for PHP developers creating Web applications, including PHP Development Tools (PDT), Web Tools Platform, Mylyn and others. More…

Downloads: 47,243

Windows

Mac OS X (Carbon)

Mac OS X (Cocoa)
Linux 32bit
Linux 64bit

An IDE for C/C++ developers with Mylyn integration. More…

Downloads: 36,326

Windows

Mac OS X (Carbon)

Mac OS X (Cocoa)
Linux 32bit
Linux 64bit

A complete set of tools for developers who want to create Eclipse plug-ins or Rich Client Applications. It includes a complete SDK, developer tools and source code, plus Mylyn, an XML editor and the Eclipse Communication Framework. More…

Downloads: 12,642

Windows

Mac OS X (Carbon)

Mac OS X (Cocoa)
Linux 32bit
Linux 64bit

This modeling package contains a collection of Eclipse Modeling Project components, including EMF, GMF, MDT XSD/OCL/UML2, M2M, M2T, and EMFT elements. It includes a complete SDK, developer tools and source code. Note that the Modeling package includes some incubating components, as indicated by feature numbers less than 1.0.0 on the feature list. More…

Downloads: 10,763

Windows

Mac OS X (Carbon)

Mac OS X (Cocoa)
Linux 32bit
Linux 64bit

JEE tools and BIRT reporting tool for Java developers to create JEE and Web applications that also have reporting needs. More…

Downloads: 9,907

Windows

Mac OS X (Carbon)

Mac OS X (Cocoa)
Linux 32bit
Linux 64bit

Pulsar is a tools platform for Mobile Java Developers. It includes the Eclipse Platform, Java Development Tools (JDT), Mobile Tools for Java (MTJ), Mylyn and Plugin Development Environment (PDE). Pulsar also makes it easy to download SDK from different handset manufacturers. More…

Downloads: 5,361

Windows

Mac OS X (Carbon)

Mac OS X (Cocoa)
Linux 32bit
Linux 64bit

The classic Eclipse download: the Eclipse Platform, Java Development Tools, and Plug-in Development Environment, including source and both user and programmer documentation. Please look also at the Eclipse Project download page. More…

Windows

Mac OS X (Carbon)
Mac OS X (Cocoa)

Linux 32bit
Linux 64bit

Bom, vou ficando por aqui, este post foi escrito para participar do Blogathon, e tentar ganhar uma jaqueta do Eclipse :D
Acho difícil um post em português ganhar, mas pelo menos uma camiseta acho que rola :D

Tags: , ,

25 Jun 09 Um especialista precisa saber um pouco de cada coisa

Para quem acha que o título deste post esta contraditório, lamento informar, mas você esta completamente equivocado.
Você conhece algum especialista? De preferência algum que esteja ai pertinho de você.
Se conhece por exemplo um especialista em Java ou .NET, chega pra ele e pergunta se ele conhece algum dos seguintes assuntos:

  • XML
  • Expressões Regulares
  • HTML
  • XHTML
  • Javascript
  • Modelos de Threading
  • Como funciona uma CPU
  • Para que serve um sistema operacional
  • O que é e para que serve uma “Maquina Virtual”
  • Flash
  • XSD
  • XPath
  • SQL
  • Estrutura de bancos de dados
  • TCP/IP
  • Sockets

Acredito que a resposta vai ser sim para todos, ou pelo menos a grande maior parte destes itens. E isto são só coisas genéricas, imagina se começarmos a detalhar a sopa de letrinhas existente no mundo Java EE ou no .NET.
Pois é mais ou menos isto que estou querendo dizer, um especialista precisa saber um monte de coisas para se tornar um especialista em uma delas.
A forma mais fácil que eu conheço para melhorar muito e muito rápido a qualidade do código que você escreve em uma linguagem é aprendendo outra linguagem de programação.
Tem gente que diz que o ideal é aprender uma linguagem nova por ano, e com certeza, o período da minha vida profissional que eu mais melhorei foi quando aprendi várias linguagens em um período curto de tempo.
Quando eu era mais novo (coisa de velho escrever isto :D ) o meu chefe na época disse que um especialista é alguem que sabe cada vez mais sobre cada vez menos, e que um super especialista é alguem que sabe absolutamente tudo sobre absolutamente nada …
Ach oque este conceito esta um pouco desatualizado, até por que por este conceito, um super especialista é o cara que sabe absolutamente tudo sobre absolutamente nada.

Pelo menos na minha opinião, eu espero que um especialista em Java por exemplo, consiga criar um pacote EAR padrão Java EE para uma aplicação composta por dois módulos web e três módulos EJB além de algumas bibliotecas utilizadas por todos os módulos.
Para fazer isto, o cara vai ter que conhecer no mínimo muito XML, vai ter que saber o que são meta dados, vai ter que saber quais meta dados foram definidos via anotações no código e quais ele vai querer sobre escrever com XML. Vai ter que conhecer a estrutura de um arquivo EAR, a estrutura de um arquivo WAR e qual a diferença entre um arquivo jar de uma biblioteca e de um módulo EJB.
Para entender direito o que ele ta fazendo, ele vai ter que conhecer o protocolo HTTP, por conseqüência o protocolo TCP e o IP. Além de precisar entender de RMI que é utilizado para chamada dos EJBs, RMI também funciona sobre TCP.
Se o servidor for rodar em cluster, é necessário saber como este cluster esta configurado, a maior parte dos servidores Java EE utiliza o protocolo IIOP/IP, o mesmo do corba, já que pela especificação Java EE todo EJB pode ser chamado utilizando CORBA também, e que o IIOP/IP permite roteamento muito mais fácil do que o RMI direto.
E isto tudo só para começar.
Se o especialista em java precisar também configurar o servidor de aplicações também ai aumenta bastante a quantidade de coisas que ele vai ter que saber só para poder ser chamado de especialista em Java e nem chegamos na parte de desenvolvimento ainda …
Claro que isto ainda é só a minha opinião, mas para ser um especialista em java, o cara tem que saber muito bem Orientação a Objetos, Reflexão, Refactoring e mais Refactoring, AOP, a diferença entre excessões checadas e não checadas, para que serve cada tipo de collection, todas as classes no mínimo dos pacotes java.lang e java.util e mais um monte de outras coisas.

Só para finalizar.
Vocês não vão conseguir se tornar especialistas em nada da noite para o dia. Isto vai demorar bastante, e mesmo que você queira ser especialista em .NET por exemplo, você vai ter que estudar muitas outras coisas.
A pior coisa que tem é programador bitolado que acha que a única linguagem/ferramenta/time/religião que presta é a que ele conhece agora …
(isto foi um misto de dicas com desabafo :D )

Tags: , ,

13 May 09 Java tem espelhos, e o mago deve saber jogar com eles (Básico da Reflexão)

Ok, o título deste post ficou meio estranho, mas como muita gente diz que isto é magia negra mesmo, então até que o título não esta tão ruim :D
Uma coisa que eu vejo bastante por ai, e não é de hoje, é que grande parte dos programadores Java não faz idéia do que seja Reflection, e normalmente tem medo de escrever, ou até mesmo de ler código “complicado”.
Não vou dizer que reflection é simples, mas é um recurso extremamente poderoso do Java que todo programador Java deveria conhecer.

O que é Reflection

Reflection em java, é como o nome diz, a possibilidade de programaticamente, visualizar um reflexo de um objeto ou uma classe, e como em um espelho, é possível também distorcer um pouco esta imagem quando necessário.
Como no reflexo em um espelho, o que você visualiza, não é o objeto real, apenas um reflexo deste, mas como na física, você pode deduzir como interagir com o objeto real, utilizando o seu reflexo.

Quando eu escrevi isto, lembrei de uma cena de um filme muito velho, acho que era “fúria de titâs” ou algo assim, onde alguem utilizava o reflexo da medusa em um escudo para lutar com ela sem se transformar em pedra.

Mas voltando ao assunto, reflection, é a possibilidade, de em tempo de execução, diversas informações sobre um objeto qualquer, incluindo mas não se limitando a seguinte lista:

  • Qual é a classe de um objeto
  • Quais métodos públicos a classe possui
  • Quais métodos foram declarados na classe (incluindo os privados)
  • Quais interfaces a classe implementa
  • Qual classe a classe do objeto estende
  • Quais anotações foram colocadas em uma classe
  • Quais anotações foram colocadas em um método
  • Quais anotações foram colocadas em um atributo
  • Quais anotações foram colocadas em um parâmetro de um método
  • Quais os tipos dos parâmetros de um método
  • Qual o tipo dos atributos de uma classe
  • Qual o tipo de retorno de um método

Via reflexão também é possível por exemplo, executar as seguintes ações em um objeto de uma classe que não existia no momento em que o código foi desenvolvido (um plugin por exemplo):

  • Instanciar um objeto
  • Chamar um método passando parâmetros ou não
  • Ler o valor de atributos privados de um objeto
  • Criar um clone de um objeto copiando o estado do mesmo
  • Chamar diretamente métodos privados de um objeto
  • Chamar métodos estáticos de uma classe
  • Ler atributos estáticos de uma classe

Claro que estes são só exemplos, a API de reflection adiciona muito mais flexibilidade do que isto, principalmente quando combinada com algum framework de AOP ou com a API de criação de Proxies disponível no próprio Java.
Mas para fazer tudo isto, é necessário conhecer algumas classes que a maior parte dos programadores Java não se preocupam em conhecer.
Só um detalhe antes de apresentar as novas classes, apenas combinando a API de reflection com a API de Proxies e o suporte a annotations do Java 5 é possível implementar todos os recursos do EJB3 por exemplo.
Agora vamos aprender a usar espelhos para fazer mágica :D

  • java.lang.Class – Esta é a classe que representa uma classe, todos os objetos tem um método “getClass” que retorna um objeto do tipo java.lang.Class, a partir de um objeto deste tipo, é possível obter diversas informações sobre uma classe, este é o ponto de entrada para o mundo dos espelhos, exatamente como aquele espelho do Alice no pais das maravilhas, mas sem o perigo de ficar preso por la como aconteceu com ela
  • java.lang.reflect.Field – Esta é a classe que representa um atributo de uma classe, objetos deste tipo nos permitem saber por exemplo, o nome dos campos de uma classe, e até mesmo ler o valor destes atributos
  • java.lang.reflect.Method – Objetos deste tipo representam métodos de uma classe, é possível utilizar estes objetos para chamar métodos passando ou não parâmetros para eles, mesmo que estes métodos sejam privados
  • java.lang.reflect.Modifier – Esta classe permite saber se um método ou atributo é privado ou publico, se ele é estático, ou seja, quais os modificadores de acesso foram utilizados na declaração do método, atributo ou classe
  • java.lang.reflect.Array – Esta classe representa um array, com objetos deste tipo é possível acessar todos os elementos de um array por exemplo
  • java.lang.reflect.Constructor – Esta classe representa um construtor de uma classe qualquer, uma classe pode ter diversos contrutores, e com referências para estes contrutores podemos saber quais os parâmetros necessários para instanciar uma objeto, ou até mesmo instanciar o objeto realmente
  • java.lang.reflect.AnnotatedElement – esta interface permite saber quais as anotações presentes em uma declaração, ela é implementada por Class, Method e Field por exemplo
  • Package java.lang.reflect – Antes de continuar lendo, por favor de uma olhada em quais as outras classes disponíveis no pacote responsável pela criação de espelhos e também pela distorção dos reflexos nestes espelhos no Java

Agora um exemplo básico, por que acredito que vocês não conseguiram entender muita coisa até aqui, mas eu prometo que depois de um exemplo as coisas vão ficar um pouco mais claras.

Este exemplo, razoavelmente simples, vai listar todos os métodos e atributos públicos em uma classe.
Para o exemplo se tornar um pouco mais divertido, o nome da classe deve ser passado como parâmetro, isto também faz você poder listar uma classe que não existia quando o programa foi compilado.

test/List.java

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
 
package test;
 
import java.lang.reflect.*;
 
public class List {
        public static void main(String[] args) throws Exception{
                if(args.length!=1)
                        throw new Exception("Voce precisa informar o nome da classe como unico parametro");
                Class<?> clazz = Class.forName(args[0]);
                printClassInformation(clazz);
                printClassAttributes(clazz);
                printClassMethods(clazz);
        }
        public static void printClassInformation(Class<?> clazz) throws Exception {
                System.out.println("Class Name: " + clazz.getName());
        }
        public static void printClassAttributes(Class<?> clazz) throws Exception {
                for(Field f : clazz.getDeclaredFields()){
                        System.out.format("\t--Private Attribute Name: %s, Attribute Type: %s\n",f.getName(),f.getType().getName());
                }
        }
        public static void printClassMethods(Class<?> clazz) throws Exception {
                for(Method m : clazz.getMethods()){
                        System.out.format("\tMethod Name: %s, Return Type: %s, Parameter Types: %s\n",m.getName(),m.getReturnType().getName(),m.getParameterTypes().toString());
                }
        }
}

Compile este exemplo, e execute passando por exemplo “java.lang.Class” como parâmetro e você vai ter um exemplo básico do funcionamento da API de Reflection.
Você pode também criar outra classe, empacotar ela em um arquivo .jar, adicionar este jar no classpath e executar este exemplo passando o nome da sua nova classe como parâmetro.

Claro que este é um exemplo extremamente básico, com um código que se não fosse pelo parâmetro passado para algumas classes, poderia ser executado até no Java 1.2, ou seja, a API de reflexão não é nova, é apenas sub utilizada pelo programador de nível médio.
Ahh, mas saber isto vai fazer com que eu seja um expert em java?
Claro que não! Mas não saber isto, com certeza te impede de ser um :D

test/Call.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
package test;
 
import java.lang.reflect.*;
 
public class Call {
        public static void main(String[] args) throws Exception {
                Class<?> clazz = Class.forName(args[0]);
                Method m = clazz.getMethod(args[1]);
                Object instance = clazz.newInstance();
                Object result = m.invoke(instance);
                System.out.println(result);
        }
}

Este é outro exemplo bastante simples, apenas para demonstrar algumas possibilidades, este exemplo bastante simples, chama um método sem parâmetros em uma classe qualquer que tenha um construtor padrão.
Para chamar métodos estáticos, seria necessária uma pequena alteração, o parâmetro passado para o método “invoke” da classe Method, é a instancia do objeto onde o método deve ser chamado, para métodos estáticos, esta instância é substituida pela classe que possui o método estático.
E o construtor padrão é necessário, por que precisamos de uma instância da classe para invocar o método, se a classe não possuir um construtor padrão, precisaremos passar parâmetros para o construtor, o que iria complicar bastante o exemplo, e a idéia aqui é só mostrar algumas possibilidades, e não complicar mais ainda a vida de vocês.
Mas como funciona o exemplo?
Tente compilar o exemplo, e executar ele passando os parâmetros:
java.lang.Object hashCode
ou
java.lang.Object toString
ou
QualquerNomeDeClasse nomeDeUmMétodoSemParâmetrosDestaClasse
e pronto, método executado.

Ai você vai pensar agora: Mas é muito mais fácil eu escrever direto “System.out.println(new Object().hashCode())” no meu código.
Claro que é, mas para isto você precisaria saber que o método que seria executado era o hashCode de uma nova instância de Object.
A idéia da API de reflection é obter informações em tempo de execução, é possibilitar um pouco de méta programação no Java.

Imagine só, criar um proxy para uma interface que garante que todos os métodos executados, caso tenham a anotação @Transactional, serão executados dentro do contexto de uma transação.
Isto é meta programação, isto é manter a mente um pouco mais aberta do que o programador médio.

Outra possível pergunta: Tu não vai ser expulso do clubinho por que esta revelando os segredos, como aconteceu com o Mister M?
Resposta: Claro que não, isto não é segredo nenhum, tu só não tinha aprendido antes por que não parou para estudar. Se você fosse um pouco mais preguiçoso, como eu, você já teria parado para estudar uma forma de trabalhar menos com as ferramentas que você tem na mão, e se você é um programador, meta programação, é uma forma de fazer mais trabalhando menos.

Mais uma pergunta: Por que ninguem me contou isto antes? explicando assim até parece fácil!
Resposta: Provavelmente achavam que tu é burro demais para entender, agora tu pode provar que isto não é verdade. E não se engane, não é tão simples assim, código usando reflexão pode ficar bastante complicado, eu só mostrei uns exemplos bem básicos.

A API de Proxies eu vou deixar como assunto para um próximo post, minha imagina imaginação esta meio fraca hoje, estou de saco cheio de assistir esta aula maluca que eu não to nem prestando atenção, acho que vou pra casa já :D

Se vocês tiverem idéias de mais exemplos que vocês querem ver como pode ser feito com reflexão, ou se tiverem perguntas sobre reflexão em java, por favor sintam-se a vontade de registrar as perguntas, dúvidas e sugestões aqui nos comentários do blog, vou tentar responder todas as perguntas :D
E como sempre, se você gostou deste post, indique para seus amigos, e coloque um link no seu blog :D

Acho que daqui a uns dias eu escrevo mais sobre reflection, mas vou tentar utilizar uns exemplos mais complexos, se tiverem sugestões para o próximo post, é só deixar nos comentários.

Tags: , , ,

11 May 09 Mensagens de erro são feias mas não mentem (nem mordem)

Já sou desenvolvedor a algum tempo (comecei em 1997, façam as contas se quiserem :D ), e uma das coisas mais importantes que aprendi até hoje é com certeza que todas as mensagens de erro geradas por linguagens de programação, frameworks, e assemelhados, são realmente feias.
Os Stack Traces do Java são realmente muito feios, chegam a assustar quem esta começando, os do Ruby não são muito melhores.
Em C++ não tem stack traces, mas os memory dumps fazem um papel parecido, e memory dumps podem ser conseguidos a partir de qualquer linguagem compilada.
O C# tem stack traces também, bem próximos do Java, acredito que isto seja parte do .NET e não uma particularidade do C#, mas eu conheço muito pouco de .Net, então agradeço se alguem puder confirmar isto.

Outra coisa bastante importante, e uma verdade absoluta, regra inquebrável, e como tal, tem pouquíssimas exceções, é que o código que você vai escrever não vai funcionar de primeira, você não é perfeito, e você vai cometer erros.
Pode acontecer de uma ou duas vezes durante a sua vida, você conseguir testar alguma coisa e esta coisa funcionar de primeira, mas eu não faria com que a minha felicidade dependesse disto, por que esta é uma situação bastante incomum.

Beleza, e o que uma coisa tem a ver com a outra?
Se você vai cometer erros, você vai precisar descobrir o que você fez de errado, e muitas das vezes, isto não vai ser fácil, e o seu melhor amigo para esta situação, a melhor ajuda que você vai conseguir, não vai ser do seu colega do lado, por mais Nerd que ele seja, vai ser a mensagem de erro/stack trace/memory dump que vai salvar a sua pele nesta situação.
Se o seu colega Nerd for te ajudar, provavelmente, ele vai perguntar: “Qual foi o erro?”
E se preste atenção nesta listinha de respostas:

  • Não sei.
  • Ah, apareceu um stack trace aqui, mas eu já apaguei.
  • Foi um erro estranho, acontece de vez em quando, mas nunca prestei atenção na mensagem.
  • Não lembro se foi null pointer ou out of memory, mas tu pode me ajudar aqui?
  • Ahh, vem aqui ver, eu não sei ler esta mensagem.

Estas são resposta inválidas, e provavelmente vão fazer o seu colega, que poderia te ajudar, ficar bastante chateado, e te ajudar com má vontade.
Para resolver este problema, você precisa aprender a ler estas mensagens de erro, isto vai te poupar muito tempo, e tudo o que poupa tempo, acaba te tornando mais produtivo, se tu for mais produtivo, o teu chefe vai gostar mais de ti, e tu vai ganhar mais, se tu for mais produtivo, tu vai terminar o que tem que fazer mais rápido, e por conseqüência, vai pra casa mais cedo :D

A leitura de mensagens de erro, seja qual for a encarnação, requer quatro coisas:

  1. Conhecimento básico do código da aplicação, ou conhecimento profundo da linguagem/framework utilizado (o primeiro é mais fácil de conseguir, mas não tenho como ajudar muito)
  2. Um pouquinho de técnica – nisto eu posso dar uma ajudinha, inclusive é esta a idéia deste post
  3. Conhecimento no mínimo básico de inglês – posso fornecer alguns links para ajudar, mas se você realmente quer continuar trabalhando com desenvolvimento de sistemas, você precisa aprender, no mínimo a lêr em inglês.
  4. Que o stack seja impresso na tela, em arquivo, mostrado em um dialog, ou qualquer coisa que facilite a visualização, sugiro logar todos os stack traces

Técnica básica para leitura de stack traces no Java

Algumas vezes, apenas ler a mensagem ja resolve o problema, como neste exemplo que peguei por ai na web:

INFO 13:37:20 [org.hibernate.connection.ConnectionProviderFactory] - Initializing connection provider: org.springframework.orm.hibernate3.LocalDataSourceConnectionProvider
WARN 13:37:41 [org.hibernate.util.JDBCExceptionReporter] - SQL Error: 17002, SQLState: null
ERROR 13:37:41 [org.hibernate.util.JDBCExceptionReporter] - Io exception: The Network Adapter could not establish the connection
WARN 13:37:41 [org.hibernate.cfg.SettingsFactory] - Could not obtain connection metadata
java.sql.SQLException: Io exception: The Network Adapter could not establish the connection
 at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:125)
 at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:162)
 at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:274)
 at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:328)
 at oracle.jdbc.driver.PhysicalConnection.(PhysicalConnection.java:361)
 at oracle.jdbc.driver.T4CConnection.(T4CConnection.java:151)
 at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:32)
 at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:595)
 at java.sql.DriverManager.getConnection(DriverManager.java:525)
 at java.sql.DriverManager.getConnection(DriverManager.java:140)
 at org.springframework.jdbc.datasource.DriverManagerDataSource.getConnectionFromDriverManager(DriverManagerDataSource.java:291)
 at org.springframework.jdbc.datasource.DriverManagerDataSource.getConnectionFromDriverManager(DriverManagerDataSource.java:277)
 at org.springframework.jdbc.datasource.DriverManagerDataSource.getConnectionFromDriverManager(DriverManagerDataSource.java:259)
 at org.springframework.jdbc.datasource.DriverManagerDataSource.getConnection(DriverManagerDataSource.java:241)
 at org.springframework.orm.hibernate3.LocalDataSourceConnectionProvider.getConnection(LocalDataSourceConnectionProvider.java:80)
 at org.hibernate.cfg.SettingsFactory.buildSettings(SettingsFactory.java:72)
 at org.hibernate.cfg.Configuration.buildSettings(Configuration.java:1859)
 at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1152)
 at org.springframework.orm.hibernate3.LocalSessionFactoryBean.newSessionFactory(LocalSessionFactoryBean.java:800)
 at org.springframework.orm.hibernate3.LocalSessionFactoryBean.afterPropertiesSet(LocalSessionFactoryBean.java:726)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1059)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:363)
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:226)
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:147)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:269)
 at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:320)
 at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:87)
 at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:72)
 at org.springframework.test.AbstractSpringContextTests.loadContextLocations(AbstractSpringContextTests.java:121)
 at org.springframework.test.AbstractDependencyInjectionSpringContextTests.loadContextLocations(AbstractDependencyInjectionSpringContextTests.java:210)
 at org.springframework.test.AbstractSpringContextTests.getContext(AbstractSpringContextTests.java:101)
 at org.springframework.test.AbstractDependencyInjectionSpringContextTests.setUp(AbstractDependencyInjectionSpringContextTests.java:178)
 at junit.framework.TestCase.runBare(TestCase.java:125)
 at junit.framework.TestResult$1.protect(TestResult.java:106)
 at junit.framework.TestResult.runProtected(TestResult.java:124)
 at junit.framework.TestResult.run(TestResult.java:109)
 at junit.framework.TestCase.run(TestCase.java:118)
 at junit.framework.TestSuite.runTest(TestSuite.java:208)
 at junit.framework.TestSuite.run(TestSuite.java:203)
 at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:128)
 at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)

Como pode ser visto na linha 5, os stack traces no Java, começam sempre pelo nome completo da classe da última exceção gerada, seguida imediatamente pela mensagem de erro, separadas por “:”.
No caso deste exemplo, precisamos apenas acreditar que não foi possível conectar no banco de dados, ocorreu algum problema de rede.
Isto nos leva a um ponto que você vai descobrir sozinho quando trabalhar com oracle por um tempo, eles não ajudam muito a descobrir qual o problema :D
Acredito que este erro tenha ocorrido por problemas de configuração da conexão com o banco de dados ou então problemas com o banco de dados real …
Mas este stack esta aqui só pra eu poder reclamar um pouquinho da Oracle :D
Não serve como um exemplo do que eu quero mostrar para vocês (que tiveram paciência de ler até aqui);
Vejam este outro stack que eu gerei de propósito como exemplo:

Exception in thread "main" java.lang.NullPointerException
	at java.io.File.(File.java:222)
	at utils.urubatan.StackTraceReadingExample.readConfigurationFromFileName(StackTraceReadingExample.java:29)
	at utils.urubatan.StackTraceReadingExample.readConfiguration(StackTraceReadingExample.java:25)
	at utils.urubatan.StackTraceReadingExample.verifyConfiguration(StackTraceReadingExample.java:21)
	at utils.urubatan.StackTraceReadingExample.connectAndExecuteQuery(StackTraceReadingExample.java:17)
	at utils.urubatan.StackTraceReadingExample.main(StackTraceReadingExample.java:11)

Na linha 1, já temos um erro bastante comum, e se você ler isto, olhar para o seu colega do lado, e reclamar que o seu codigo gera um NullPointerException sem dizer o que esta acontecendo, por favor, desista de programar agora, antes que você fique realmente frustrado, ou se for muito insistente, coloque o seu amigo na cadeira de um psicólogo achando que trabalha com retardados :D
Este stack é até bem fácil, e serve para demonstrar o que eu quero …
Para ler um Stack trace, comece a ler de traz para frente, ou seja, leia normalmente de cima para baixo, e pare de ler na primeira linha em que o nome da classe pertencer ao seu projeto.
Neste caso, isto ocorre na linha 3 do stack trace: utils.urubatan.StackTraceReadingExample.readConfigurationFromFileName(StackTraceReadingExample.java:29)
Onde podemos ver que o erro esta sendo gerado no método “readConfigurationFromFileName”, da classe “StackTraceReadingExample”, na linha 29 do arquivo “StackTraceReadingExample.java”, ou seja, para corrigir o problema vamos para esta linha ver o que acontece lá, segue o código do exemplo:

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
29
30
31
32
33
34
35
36
37
38
39
40
package utils.urubatan;
 
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
 
public class StackTraceReadingExample {
	public static void main(String[] args) throws IOException {
		StackTraceReadingExample ex = new StackTraceReadingExample();
		ex.connectAndExecuteQuery();
	}
	private String fileName;
	private Properties configuration;
 
	private void connectAndExecuteQuery() throws IOException {
		verifyConfiguration();
	}
 
	private void verifyConfiguration() throws IOException {
		readConfiguration();
	}
 
	private void readConfiguration() throws IOException {
		readConfigurationFromFileName(fileName);
	}
 
	private void readConfigurationFromFileName(String theFileName) throws IOException {
		FileReader fr = new FileReader(new File(theFileName));
		createEmptyConfigurationIfNeeded();
		configuration.load(fr);
		fr.close();
	}
 
	private void createEmptyConfigurationIfNeeded() {
		if (configuration == null) {
			configuration = new Properties();
		}
	}
}

Na linha informada, a única variável que esta sendo utilizada é o nome do arquivo, que se formos ler o código, realmente nunca foi inicializado, para resolver este problema, basta que alteremos a linha 13 para inicializar a variável para algum nome de arquivo, vou adicionar: = “teste.config” e vamos ver o que acontece.

Depois desta alteração continuamos com um erro, e este stack continua bastante simples, mas é um pouco mais complicado que o anterior:

Exception in thread "main" java.io.FileNotFoundException: teste.config (The system cannot find the file specified)
	at java.io.FileInputStream.open(Native Method)
	at java.io.FileInputStream.(FileInputStream.java:106)
	at java.io.FileReader.(FileReader.java:55)
	at utils.urubatan.StackTraceReadingExample.readConfigurationFromFileName(StackTraceReadingExample.java:29)
	at utils.urubatan.StackTraceReadingExample.readConfiguration(StackTraceReadingExample.java:25)
	at utils.urubatan.StackTraceReadingExample.verifyConfiguration(StackTraceReadingExample.java:21)
	at utils.urubatan.StackTraceReadingExample.connectAndExecuteQuery(StackTraceReadingExample.java:17)
	at utils.urubatan.StackTraceReadingExample.main(StackTraceReadingExample.java:11)

Agora o erro esta na 5a linha do stack, que é a primeira linha com código fonte da aplicação …
Esta é a técnica básica para ler stack traces:

  1. Leia a mensagem de erro na primeira linha, as vezes ela pode te ajudar, se a mensagem não ajudar, você pode anotar o nome da classe do erro, que algumas vezes também já é informação o suficiente
  2. Logo em sequida, leia o stack de traz para frente (que é a mesma coisa que de cima para baixo no Java) e procure a primeira linha de código da tua aplicação, ou do framework que você esta utilizando, dependendo do caso e do erro que esta acontecendo, assim você acabou de localizar a linha de código que esta gerando o erro
  3. Se isto ainda não for o suficiente (muitas vezes não é), seguindo pelo Stack, mais para baixo, você pode literalmente voltar no tempo e saber quem chamou aquele método até o início dos tempos
  4. Se apenas ler o código não resolver, utilize a informação obtida no passo anterior, para saber onde colocar o “break point” e utilize o debugger da sua IDE para encontrar o problema, ou pelo menos a raiz do problema

Com estes passos, os stack traces vão te ajudar bastante, algumas IDEs como o Eclipse por exemplo, imprimem os stack clicaveis no console, ou seja, você clica em uma linha do stack trace, e o eclipse abre o arquivo, na linha em que o erro ocorreu.

Estes passos servem também para builds ANT, para programas escritos em action script (Flash ou Flex), para programas escritos em Ruby, incluindo o Rails.
E com pequenas adaptações, funciona também para C++, C, qualquer outra linguagem que gere algo parecido com um stack trace.

Agora uma perguntinha, só pra não perder o costume, você que leu até aqui, acha que valeu a pena a leitura? tem algum colega que você gostaria de poder obrigar a ler isto? ou tem algum exemplo que não se enquadra no que eu escrevi?
Eu tenho alguns amigos que eu gostaria de obrigar a ler isto, ou então abrir a cabeça e jogar isto para dentro, mas infelizmente eu não posso fazer isto.
Se você acha que o texto ficou bom, indique a leitura, se acha que precisa melhorar alguma coisa, deixe nos comentários que eu incorporo a melhoria no texto do post :D

PS.: este versionamento de posts do WP até que é legal, apaguei tudo sem querer, e acho que consegui recuperar legal com ele :D

Tags:

11 May 09 Em time que esta ganhando se mexe sim (Refactoring básico)

Por mais que você seja um excelente programador, que todo o seu código funcione perfeitamente na primeira vez em que é executado (o que eu acho bem pouco provável que aconteça), por mais que você conheça pouco do código do sistema, ou por qualquer outro motivo que você possa lembrar agora ou daqui a 10 anos.
Em código que esta funcionando se mexe sim!
Mas por que estou dizendo isto? Porque se você fizer como eu, e de vez em quando, mas só de vez em quando para não ficar muito decepcionado consigo mesmo, pegar algum código que você escreveu no mês passado, ou a seis meses atrás, ou a um, dois, cinco ou dez anos, você vai achar este código muito mal escrito, mal organizado, feio, escrito por alguém que ainda precisava aprender tudo o que você aprendeu neste intervalo.
Se isto não acontecer com você, com certeza você está se tornando um programador medíocre que não aprendeu absolutamente nada neste intervalo, que não está melhor hoje do que era na semana passada, ou no mês passado ou no ano passado.
Esta sensação de que o código velho é ruim, não quer dizer que você era um programador ruim, é apenas o sinal de que você se esforçou e que hoje você é muito melhor do que era quando escreveu aquele código.

Ok, e o que isto tem a ver com este post? Tudo!
Se este código velho faz parte de algum sistema, biblioteca, projeto ou qualquer coisa do gênero que você não trabalha mais, deixe da forma como está, as pessoas que estão trabalhando nele agora que se preocupem com ele. Mas se ao contrário ele ainda faz parte de um código que você evolui dia a dia, então é sua responsabilidade fazer com que este código velho e maltrapilho, escrito por você ou não, evolua também, pelo menos o suficiente para não atrapalhar o código novo, escrito por este programador muito melhor do que aquele que escreveu o lixo que está sob seus olhos, mesmo que este tenha sido você ontem.
E este ato de piedade, generosidade e auto compaixão, é chamado de refactoring.
Ou seja, você vai fazer com que o código legado, melhore, seja mais testável, mais estável, mais bonito, sem quebrar todo o resto do sistema que já depende daquele pedaço de lixo que você escreveu no passado :D
E por que isto é também um ato de “auto compaixão”? Porque como eu ouvi um amigo comentar várias vezes, assim você esta diminuindo a quantidade de problemas legados que você vai ter que lidar no futuro próximo.

O que é rafactoring

Segundo a wikipedia: Refatoração (do inglês Refactoring) é o processo de modificar um sistema de software para melhorar a estrutura interna do código sem alterar seu comportamento externo.

Segundo o papa: Refatoração é uma técnica disciplinada para reestruturar um corpo de código existente, alterando a sua estrutura interna sem alterar o seu comportamento externo. O coração da técnica é uma série de pequenas transformações preservando o comportamento. Cada transformação (chamada de refactoring) faz um pouco, mas uma série de transformações podem produzir um resultado significante para a qualidade do sistema. Já que cada refatoração é pequena, é menos provável que ela cause algum problema. O sistema é mantido 100% funcional depois de cada refactoring. Reduzindo as chances de algum problema grave no sistema no final da reestruturação.

O que indica que um código precisa ser refatorado?

Basicamente se você acha que pode melhorar o código, isto já vale o rafactoring. Você achar que pode melhorar o código quer dizer que você esta sentindo que tem alguma coisa errada com ele, mesmo que você não tenha muita certeza de o que esta errado. Isto só quer dizer que você já aprendeu mais coisas depois que escreveu o código que esta lendo agora.
Mas podemos também formalizar isto um pouco, ou seja, definir alguns pontos nos quais todos concordam haver problemas no código, estes servem como argumento inclusive para você dizer que o código dos outros não esta muito bom, ou para você ter certeza de que o seu esta um lixo logo depois que escreve-lo.

  • Código duplicado (duplicated code)
  • Método longo (long method)
  • Classe grande (large class)
  • Lista de parâmetros longa (long parameter list)
  • Má indentação (Bad Indentation)

Os nomes em inglês estão ali por que eu não inventei isto, eu retirei esta pequena lista do livro do Fowler sobre rafactoring, também referenciado como “a biblia” pelo menos por mim :D

Alguns exemplos de refactoring

Ok, agora que você já sabe como identificar alguns problemas (é fácil, é só você achar que hoje sabe mais do que ontem :D ), vamos ver algumas soluções engarrafadas, prontinhas para beber, ou utilizar na sua IDE preferida (todas as IDEs Java hoje em dia possuem um suporte muito bom para refactorings).

  • Adicionar parâmetro – Add Parameter
     Muitas vezes é necessário adicionar mais um parâmetro em um método já existente, quando fizer isto, sera necessário alterar todos os lugares que já chamam o método existente, ou pelo menos deixar o método original como um delegate passando um valor padrão para o novo método, tudo depende de por que você esta utilizando este refactoring
  • Converter inicialização dinâmica por estática – Convert Dynamic to Static Construction
     Algumas vezes a utilização de reflection pode criar fraquezas para o sistema, se isto não for necessário, então prefira inicializar estaticamente os objetos
  • Converter inicialização estática por dinâmica – Convert Static to Dynamic Construction
     Algumas vezes o sistema tem bastante a ganhar com a utilização de reflection para reduzir código duplicado ou pelo menos para adicionar flexibilidade
  • Encapsular atributo – Encapsulate Field
     Nunca, e eu disse nunca mesmo, exponha um atributo diretamente em java, isto simplesmente não esta certo, prefica utilizar métodos de leitura e escrita
  • Extrair classe – Extract Class
     Várias vezes é possível encapsular parte do comportamento de uma classe maior em outra classe
  • Extrair interface – Extract Interface
     Algumas vezes acessar diretamente uma classe de outras partes do sistema aumenta muito o acoplamento do sistema, nestes casos, prefira criar uma interface com os métodos públicos e esconder um pouco a implementação, ou até mesmo criar mais de uma implementação
  • Extrair metodo – Extract Method
     Diversas vezes, quando o código de um método esta muito grande, é possível extrair parte dele para um outro método, preservando o comportamento e reduzindo a duplicação de código
  • Extrair sub classe – Extract Subclass
     Quando parte do comportamento de uma classe é utilizado apenas em situações especificas, criar uma sub classe pode ser uma boa idéia
  • Introduzir “objeto parâmetro” – Introduce Parameter Object
     Quando um método tem muitos parâmetros, algumas vezes é útil encapsular estes parâmetros em um objeto, e faer com que o método receba apenas aquele objeto como parâmetro
  • Mover classe – Move Class
     Quando um package tem muitas classes, algumas vezes é útil mover uma ou mais classes para outro package, também pode ser utilizado quando uma classe simplesmente esta no lugar errado.
  • Subir método – Pull Up Method
     Quando um método é implementado em mais de uma classe e estas classes compartilham uma super classe, pode ser útil mover a implementação deste método para esta super classe
  • Remover parâmetro – Remove Parameter
     Algumas vezes um dos parâmetros de um método simplesmente não é mais utilizado
  • Remover método de escrita – Remove Setting Method
     Quando um atributo deve ser somente leitura, não é interessante que exista um método de escrita para este atributo
  • Renomear método – Rename Method
     Algumas vezes o nome de um método simplesmente não parece certo, ou então não diz o que aquele método faz.


Claro que estes não são os únicos refactorings existentes, esta é apenas parte da lista que pode ser encontrada no site do livro de refactorings do Martin Fowler. Apenas as descrições foram escritas por mim, e mesmo no site, ou no livro do Fowler você não vai encontrar uma lista completa, por que é bem possível que outro refactoring seja criado hoje ou amanha, o importante é entender a idéia.

E eu tenho que decorar tudo isto? Tenho que fazer na mão e garantir que funciona?

Uma das coisas boas de ótimas idéias é que muita gente gosta delas, e acaba copiando.
Em algum momento do passado remoto do desenvolvimento java, quando as boas IDEs eram todas pagas, o pessoal da JetBrains, leu “a biblia” e disse: Que o IntelliJ IDEA ajude os desenvolvedores a fazerem os rafactorings para que o código se torne bonito e legível. E assim fez o IntelliJ IDEA.
Algum tempo depois, o eclipse copiou a idéia e passou a suportar muitos refactorings também, e hoje em dia o NetBeans também suporta muitos refactorings, e assim o desenvolvedor vive feliz podendo ser produtivo com a sua IDE favorita.
Eu adoro o IntelliJ IDEA, mas utilizo muito mais o eclipse, e algumas vezes até o NetBeans.
Todas as 3 IDEs suportam refactorings, e até o VIM e o Emacs suportam refactorings, mas não se iludam desenvolvedores Java, o pessoal da microsoft viu que isto era bom, e também adicionou suporte a diversos refactorings no visual studio, por tanto vocês não são mais os únicos com boas ferramentas.
Então respondendo a pergunta do título, não precisa decorar tudo, e não precisa fazer tudo na unha não, mas por favor, decore pelo menos os atalhos para os refactorings suportados pela sua IDE, o seu código agradece.
Outro dia eu coloco uma lista de atalhos de cada IDE para alguns refactorings por aqui (se der tempo :D )

Mas só isto basta? Meu código não vai parar de funcionar mesmo?

Um dos valores do Extreme Programming é a coragem, e este valor é necessário para se refatorar o código por exemplo, a probabilidade de o seu código parar de funcionar depois de um refactoring com a ajuda da sua IDE é bem pequena, um refactoring manual é mais arriscado, mas o código limpo bonito e funcional vale o risco.
Outra coisa, para diminuir o risco, escreva testes para o seu código sempre, com os testes, você tem algo para garantir que você não quebrou nada enquanto estava refatorando.
Mas se o mariquinhas ai não tem coragem de mexer no próprio código, pode continuar escrevendo código mediocre por ai, deve ter alguem com coragem o suficiente para fazer um trabalho decente e entregar código de qualidade na sua empresa, só espero que o seu chefe não se preocupe muito com qualidade, se não o teu medinho vai custar o teu emprego, por que com certeza, esta tua frescura já ta custando a qualidade do teu trabalho sua franguinha!

Sem brincadeiras agora, nem toda hora é hora de refatorar, nem todo refactoring vale a pena, nem sempre se tem tempo para melhorar o que já esta pronto, mas ler o código e saber o que pode ser melhorado, e como melhora-lo vai garantir que você faça menos porcaria no futuro, pelo menos comigo isto funciona :D

Tags: , ,