14 May
Aqui na nossa série de linguagens de programação (sem posts já faz um tempo), já falamos de Ruby, mas ainda não de Python. O ideal seria falar de Python antes mas, graças a uma palestra interessante que assisti online, resolvi fazer alguns comentários e críticas a cada uma delas.
Antes de mais nada, é importante entender quais as bases e as motivações de cada uma dessas duas linguagens. Ambas as linguagens têm como metas permitir código bonito, legível e o mais simples possível. Mas o critério de bonito e legível difere para cada uma delas. De acordo com o Zen do Python (numa tradução livre):
[...]
Casos especiais não são tão especiais para quebrarmos as regras.
[...]
Quando se deparar com uma ambiguidade, não ceda à tentação de tentar adivinhar.
Deveria haver uma, e preferencialmente só uma, maneira óbvia de fazer algo.
[...]
Se a implementação é difícil de explicar, não é uma boa ideia.
[...]
Já o Ruby, de acordo com o próprio criador Yukihiro Matsumoto:
[...] é simples na aparência, mas muito complexo no interior, tal como o corpo humano.
Nota-se que isso contraria a parte da simplicidade de implementação do Zen do Python. E nota-se isso também em bibliotecas famosas escritas em Ruby, como o ActiveRecord, por exemplo. Você já tentou entender a implementação da classe ActiveRecord::Base?
Além disso, Ruby herdou de Perl, uma de suas fontes de inspiração, a ideia de ter várias formas de fazer uma mesma coisa. E também tem alguns casos especiais de sintaxe, como a invocação de um setter (que pode ser com espaço antes do igual, apesar de o nome do setter não ter esse espaço), os parênteses opcionais na invocação de um método ou as chaves opcionais num hash passado como argumento.
Agora, minha opinião
Acho que é bom ter mais de um jeito de fazer a mesma coisa, como o Ruby possibilita. Um jeito só de fazer a mesma coisa, a meu ver, tem a vantagem de facilitar um pouco a leitura do código, graças à uniformidade. Mas, por outro lado, nos comunicamos uns com os outros numa língua que permite expressar uma mesma ideia de muitas formas diferentes, e mesmo assim nos entendemos, não? É claro que às vezes com alguma dificuldade, mas na maioria das vezes por causa da ambiguidade da língua. E liberdade de expressão também é importante em programação!
Por outro lado, as peculiaridades da sintaxe de Ruby não me agradam; acho que tornam a linguagem mais difícil de compreender. Os casos especiais são muitos e, às vezes, deixam o programador confuso quanto à necessidade de chaves, por exemplo.
Uma característica muito importante que a linguagem Ruby deveria ter, mas só Python tem, são argumentos rotulados. Em Ruby, costuma-se utilizar um hash para suprir essa necessidade, o que, convenhamos, não é muito elegante.
Um ponto sobre o qual ainda não tenho uma opinião muito elaborada é sobre a modificação de classes e métodos em tempo de execução. Em Ruby, ela é implícita: se você declarar uma classe com o mesmo nome de uma que já existe, você estará modificando a existente. Em Python, ela é explícita: você só pode modificar a classe existente se você escrever código específico para isso; se você declarar uma classe com o mesmo nome de uma já existente, você está criando outra classe. Por um lado, acho bom deixar explícito que você está modificando uma classe existente. Mas, por outro lado, faz sentido ter duas classes diferentes com o mesmo nome?
E você, qual a sua opinião? Quais são suas críticas a cada uma dessas linguagens?
Posts Relacionados:
Acompanhe-nos por
RSS, por Email ou via Twitter.
Veja como ter um desconto no Dreamhost: um excelente servidor web.
17 Apr
Aplicações para a Internet que se comunicam umas com as outras é algo mais do que comum. Ou sua aplicação consome algo que outra produz ou ela produz algo para outra consumir.
É bem fácil testar o que sua aplicação produz. Basta subir um servidor local, fazer uma requisição para o endereço correspondente e processar o resultado. Mas, por outro lado, como testar que sua aplicação consome o que outra produz adequadamente?
É claro que não vamos querer depender da aplicação remota de verdade. Queremos deixar os testes automatizados o mais isolados possível dos recursos externos. Então vamos, provavelmente, querer que o objeto responsável por fazer as requisições para fora não as faça de verdade e que devolva dados semelhantes aos da aplicação real.
Um jeito de fazer isso seria “embrulhar” a comunicação com a Internet numa classe nossa e, nos testes, mockar essa classe e passar essa classe mockada para as classes (ou os métodos) que precisam dela. É possível fazer isso em qualquer linguagem que suporte orientação a objetos.
Em Ruby ainda temos uma outra possibilidade. Como as classes são abertas, podemos re-escrever os métodos da classe Net::HTTP, por exemplo. Assim, não precisamos criar um embrulho; podemos utilizar sempre a classe do sistema.
Pois também não precisamos fazer isso na mão. A gem FakeWeb permite mockar requisições para determinadas URIs com um código bastante sucinto. Por exemplo, se você quiser que seu programa receba “olá mundo” quando fizer uma requisição do tipo GET para a URI http://foo.com/bar, basta escrever antes do código que executa a requisição:
FakeWeb.register_uri(:get, "http://foo.com/bar", :body => "olá mundo")
Em Java também é possível fazer algo parecido com um pouco de magia negra, desde que controlemos a criação do objeto responsável pela requisição. Mas, a meu ver, a primeira abordagem resulta num código mais desacoplado. Qual a sua opinião?
Posts Relacionados:
Acompanhe-nos por
RSS, por Email ou via Twitter.
Veja como ter um desconto no Dreamhost: um excelente servidor web.
17 Jan
Eu sou um grande fã de automatização de tarefas repetitivas. Odeio ter que repetir tarefas.
Isso me torna um grande fã de coisas como integração contínua, deploy contínuo, testes e automatizadores de builds.
Para IC, o cruisecontrol.rb é maravilhoso. Deploy contínuo eu resolvo com um simples script bash. Quanto aos testes, o infinitest (ainda vou falar mais sobre ele, mas ele é demais!) trouxe alegria de volta a minha vida.
Mas quando chega na parte de automatização de build, o mundo desaba. Como minha maior experiência é com Java, vou falar apenas sobre java.
Convenhamos, os dois automatizadores mais usados são terríveis. Tanto ant quanto maven nos fazem perder mais cabelos e mais cedo do que deveria.
Como a gente começa um projeto que vá usar um desses automatizadores? Pegamos um projeto antigo, copiamos o xml de configuração, colamos, apagamos uma parte, mudamos outra e pronto. Temos um novo projeto. Mas porque raios a gente precisa dessas coisas que foram copiadas? No ant porque o ciclo básico de muitos projetos é igual. No maven porque ele exige muita coisa quase inútil.
Eu já não gosto de xml (na verdade, acho xml muito legal, o problema é que usam ele para tudo - e na maioria das vezes, usam mal) e tenho que manter xmls muito parecidos entre todos os meus projetos. Um problema sério que o ant tem é que tudo tem que ser configurado ou ele não faz nada. Você acaba tendo que programar o seu build em xml (o que pra mim já elimina ele da minha vida).
O maven facilita um pouco nesse lado. Como ele tem um mínimo de convenções, com apenas umas 15 linhas de xml eu consigo convencer ele a compilar meu código. Mas são 15 linhas inúteis. Ele simplesmente não funciona sem essas 15 linhas.
Enquanto você apenas usa o maven para compilar seu código, rodar testes unitários, gerênciar suas dependências (sim, como todo usuário de maven eu passo por um classpath hell -em geral causado por projetos com dependências mal configuradas- por mês, mas ainda acho que vale a pena não precisar adicionar na mão), gerar a configuração para sua IDE, gerar seus jar/war, é tudo lindo e maravilhoso. O problema começa quando o projeto começa a ganhar um pouco mais de massa.
Já tentou configurar um build separado para testes de integração? mvn integration-test é uma das maiores mentiras já contadas. Você tem que se matar com o plugin do surefire (test runner que o maven usa) para conseguir separar os testes. E se você quiser um build separado para testes de aceitação? Pior ainda. Não existe mvn acceptance-test. Então ou você roda junto com o integration-test ou apela para profiles e reduz sua vida em alguns anos. E se eu sonhar com um build que rode testes de regressão para segurança (que não faz sentido rodar a cada commit. Uma vez por dia é o suficiente em muitos projetos). Nesse ponto você começa a pensar em suicídio.
E deploy? Ha! Quando você pensa isso alguém pula de trás do monitor, grita “Pegadinha do Malandro!” e te manda procurar o cargo plugin, que não vi funcionar com menos de 100 linhas de xml (quando funcionou).
Um padrão que eu notei que vem acontecendo comigo é que eu uso sempre três automatizadores. O maven para gerênciar dependências, compilar, rodar testes (mesmo com o inferno dos profiles). Scripts bash/ruby/perl/python/o_que_der_na_telha para deploy. E make para juntar tudo isso. Como? Make? Que o pessoal usa pra C? Esse mesmo. O make é simplesmente um monte de alias para um monte de comandos. Perfeito para juntar coisas completamente aleatórias.
Acabo tendo coisas como make test, integration-test, acceptance-test, deploy, etc. Mas o monte de lixo continua ali, escondido, esperando o próximo classpath hell para se auto-destruir.
Meu principal ponto com o maven é que ele tenta fazer muito mais do que deveria. Na boa? Não tem como escrever um plugin genérico para fazer deploy. Todos os sistemas são muito diferentes um do outro. Então não faça algo que vai funcionar mal e que seja difícil. Deixa para o programador se virar. Apenas facilite isso.
Bom, não escrevi esse monte de coisas apenas como um desabafo (embora tenha me feito muito bem :) ). Eu vou começar a desenvolver um automatizador que tenha uma filosofia diferente os atuais e facilite manter a minha zona organizada. Sem configuração alguma (tirando gerênciamento de dependências que não dá para evitar em qualquer projeto que use testes) ele vai ser capaz de compilar seu código, rodar qualquer tipo de teste (unit, integration, acceptance, etc). Ciclo de vida modificável (se eu não quiser que ele rode os testes unitários antes dos de aceitação, ele simplesmente não roda). O deploy fica a cargo do programador, mas o automatizador facilita a execução dos scripts. Ah, eu disse que não será usado xml algum para configuração?
Enfim, o que preciso nesse momento é de pessoas que estejam afim de contribuir com idéias e críticas (do tipo “não gosto de X porque Y”) pra esse planejamento inicial. Não criei uma lista ainda porque não sei se existem outros interessados (eventualmente sou eu que sou incapaz de usar ant e maven). Por enquanto podemos começar pelos comentários mesmo :)
Posts Relacionados:
Acompanhe-nos por
RSS, por Email ou via Twitter.
Veja como ter um desconto no Dreamhost: um excelente servidor web.
13 Jan
Um geek, por definição, é alguém que gosta de aprender coisas novas sempre: novas tecnologias, como as coisas funcionam, por aí vai. E qual o melhor jeito de aprender? Com um professor ou mentor, alguém para te explicar os comos e os porquês. Mas nem sempre esse mentor tem esse papel explicitamente. No dia-a-dia, no trabalho, na faculdade ou até mesmo em casa, sempre tem alguém que sabe alguma coisa melhor que você e pode te ensiná-la, e é preciso humildade para reconhecer isso.
O verdadeiro geek, aquele que realmente gosta de aprender, está pronto para aprender a qualquer hora com qualquer um. Ele reconhece quando alguém tem algo para lhe ensinar e vê com felicidade uma oportunidade assim. Fica feliz quando alguém o corrige, porque assim ele está aprendendo. O verdadeiro geek é humilde para ver todas as pessoas como possíveis professores de alguma coisa.
Posts Relacionados:
Acompanhe-nos por
RSS, por Email ou via Twitter.
Veja como ter um desconto no Dreamhost: um excelente servidor web.