quarta-feira, março 22, 2006

APIs que não mordem.

Desde que comecei a programar em Ruby ou outras linguagens de script, como php, por exemplo, penso sobre como essas linguagens possuem APIs bem simples e diretas de usar. Nada de muitos passos para realizar operações relativamente simples, as interfaces são mais diretas e geralmente invocar um método resolve o problema, ou boa parte dele. Mais ou menos como mostrei em um post anterior que apresenta, em 5 linhas, como fazer um leitor de RSS em Ruby. Nos ultimos dias, depois de ter conversas com Thiago Arrais e com Gandhi sobre esse assunto, resolvi ler (aqui, aqui, aqui e aqui) o que povo tem comentado sobre APIs, apenas para tentar formar uma opinião mais fundamentada. Também tentei ler algumas APIs em Java para fazer algumas operações que considero bastante diretas, como ler um RSS ou fazer o parse de um arquivo YAML.

Enquanto lia, constatei que a comunidade Java realmente cria ótimas APIs, boa parte pode servir como exemplo/estudo para programação e design patterns. Pode-se encontrar ótimo design em APIs como a de Collections ou IO, em um número bastante vasto de projetos open source e por aí vai. Mas, geralmente, as APIs em Java pecam por se utilizarem demais de expressões que se tornaram idiomaticas para a linguagem, ou pecam pelo excesso de zelo para escrever APIs que possam ser expandidas de acordo com a necessidade o que, de fato não as impede de ser simples. Um fato engraçado é como nem sempre as pessoas percebem que criam complicações desnecessarias. Exemplo: esses dias procurava por uma API para fazer o parse de arquivos YAML, se fizesse uma correspondencia direta do formato de um arquivo para a estrutura de uma classe, acharia perfeito. Eis que me deparo com a yaml4j - a YAML Loader/Dumper for Java. For those of us who wish Java were agile. O "agile", deixe-me explicar, fica por conta da discussão entre o formato yaml e xml. O primeiro exemplo da API é simples:
File f = new File(args[0]);
Reader reader = new BufferedReader(new FileReader(f));
Loader loader = new Loader();
loader.load( reader );

Object doc = loader.getDocument();
Claro, não vou colocar os imports e tudo mais, apenas o uso mais direto. Nada demais, certo? Errado! O yaml4j peca por me fazer criar objetos dos quais eu não quero saber, apenas para dar suporte a interface provida, ou melhor para superar a barreira criada pela interface não oferecida. O que eu realmente gostaria de fazer é:
Loader loader = new Loader();
loader.load("C:/my_file.yml");
Object doc = loader.getDocument();
Ou mais direto ainda:
Object doc = Loader.load("C:/my_file.yaml");
É provavel que muita gente diga que aí eu não poderia usar outros tipos de Reades, ou seja lá o que for, para fazer o load do arquivo. Não é verdade, simplesmente. Não o é porque quero apenas uma interface mais direta e não uma mais resumida, por mim o exemplo anterior poderia existir à vontade, mas a API deveria me prover métodos mais diretos. Outros com certeza dirão que, por ser código Java, eu poderia criar uma pequena extensão para a API, parar de reclamar e resolver o problema. Também não é verdade, ao menos para mim, pois estou preocupado em resolver os problemas do meu dominio e não em escrever linhas e mais linhas para dar suporte a API.

O ponto realmente é: se algo tem chance de ser usado com uma freqüência muito grande, vale a pena livrar as classes clientes da tralha, esconder a complexidade e prover uma interface para operações mais diretas. Exatamente assim:
require 'yaml'
doc = YAML::load(File.open('C:/my_file.yml'))
Ou, para ninguém ficar na bronca, para não pensarem que quero ver Java morta e enterrada, um exemplo da APIs que usei (alterei apenas para facilitar a leitura, mas perceba: é um correspondente direto para o código em Ruby):
File file = new File("ReceiptEntry.yml");
Entry entry = Yaml.loadType(file, Entry.class);
Um outro exemplo para mim é o Mentawai, apesar de o último release apresentar bons métodos para facilitar o uso do ActionConfig, poderia prover métodos como addForward, addRedirect, addNull. São consequences muito usados, então, por que não facilitar a vida? O addConsequence continua para permitir consequences customizadas.

Enfim, boa parte dos desenvolvedores Java - outras linguagens também, eu sei - deveria aprender a manter as APIs que desenvolvem tão simples quanto possível, mesmo que isso implique a criação de metodos a mais, um pouco de esperteza vai evitar copy and paste. Isso vale para desenvolvimento de classes que não formam uma API propriamente dita, mas que podem muito bem oferecer uma interface mais amigavel. As APIs nem sempre precisam estar prontas para salvar o mundo, às vezes precisam apenas carregar um arquivo YAML.

valeuz...

7 Comentarios:

Blogger Thiago Arrais disse...

Sem falar que estas expressões idiomáticas são uma ótima desculpa para repetir código. Este código de abertura de arquivo necessário para o uso da API para YAML, por exemplo, pode começar a "brotar" nos lugares mais inusitados. E quem fez isso vai simplesmente dizer algo do tipo "Deixa pra lá, isso é só o jeito Java de fazer a coisa. É uma expressão idiomática altamente difundida." Se você reclamar, pode ser até que digam que é você que não sabe programar.

Acho que não é preciso dizer que repetição de código não é o que queremos. Definitivamente.

9:33 AM  
Blogger Unknown disse...

Tem um artigo excelente sobre isso do Brian Foote. A página é horrível, a idéia de "sobrevivência" é meio esquisitona, mas os padrãoes são excelentes:

http://www.laputan.org/selfish/selfish.html

9:41 AM  
Anonymous Anônimo disse...

Opa, eu sinceramente adoro essa forma como são feitas as APIs do java. Acho que quando tu vai distribuir uma API, tu tens que deixar ela o mais aberta possivel para novas implementações, cabe ao usuário implementar coisas novas a seu gosto nesse caso a API não fica amarrada há algumas facilidades que ela poderia prover. Quando tu abstrai demais um problema, tu infelizmente acaba escravizando novas implementações que possam vir por causa dessa abstração que tu fizestes. Uma abstração geralmente encolhe passos necessários para fazer determinada coisa mas quando tu tens a necessidade de se mudar alguma coisa no núcleo(bug, atualização) da API, irremediavelmente tu acaba tendo que fazer gambiarras para remendar a abstração que a API te proveu anteriormente.

Falou...

9:45 PM  
Anonymous Anônimo disse...

Camarada, vai aprender a programar, vai.

3:51 PM  
Blogger Marcos Silva Pereira disse...

> Camarada, vai aprender a programar, vai.

Anonymous (bonito nome), eu bem que tento. Eu bem que tento. :-D

valeuz...

7:59 PM  
Anonymous Anônimo disse...

Giuliano, abstrair não quer dizer necessariamente que você está impedindo a extensibilidade ou a flexibilidade ;)

4:44 PM  
Blogger Thiago Arrais disse...

> thiago arrais said...
> Se você reclamar, pode ser até que digam
> que é você que não sabe programar.

> Anonymous said...
> Camarada, vai aprender a programar, vai.

Coincidência?

11:57 AM  

Postar um comentário

<< Home