Minha palestra Como Fazer o Design de uma Boa API e Porque isso Importa sempre atraiu grandes multidões; foi o terceiro conetúdo mais visto na InfoQ no ano passado. Quando apresentei essa sessão como palestrante convidado na OOPSLA 2006, tive a oportunidade de escrever um resumo para os anais. Ao invés de um simples resumo decidi tentar algo um pouco incomum: destilei a essência da palestra em uma modesta coleção de máximas diretas, no espírito das clássicas Notas sobre Ciência da Computação de Jon Bentley, Item 6 de seu excelente livro, More Programming Pearls: Confessions of a Coder (Addison-Wesley, 1988).
Espero que essas máximas provenham um resumo conciso dos principais pontos relacionados a design de APIs, de uma forma facilmente digerível:
Todos os programadores são designers de API. Bons programas são modulares e fronteiras entre módulos definem APIs. Bons módulos são reutilizados.
APIs podem estar entre suas grandes posses ou débitos. APIs boas criam clientes no longo prazo; APIs ruins criam pesadelos de suporte no longo prazo.
APIs públicas, como diamantes, são eternas. Você tem uma única chance de acertar, então faça seu melhor.
APIs devem ser fáceis de usar certo e difíceis de usar errado. Deve ser fácil fazer coisas simples; possível fazer coisas complexas; e impossível, ou ao menos difícil, fazer coisas erradas.
APIs devem ser auto-documentadas: Deve ser raro haver necessidade de documentação para ler código escrito usando uma boa API. Na verdade, deve ser raro haver necessidade de documentação para escrevê-lo.
Ao desenvolver uma API, primeiro levante os requisitos—com uma dose saudável de ceticismo. As pessoas comumente provêem soluções; é seu trabalho descobrir os problemas originais e achar as melhores soluções.
Estruture os requisitos como casos de uso: eles são o padrão com o qual você vai comparar sua API.
Os primeiros rascunhos de APIs devem ser curtos, tipicamente uma página com classes e assinaturas de métodos e descrições de uma linha. Isso facilita reestruturar a API quando você não acerta de primeira.
Codifique os casos de uso usando sua API antes de implementá-la, mesmo antes de especificá-la apropriadamente. Isso irá evitar que você implemente, ou até mesmo especifique, uma API fundamentalmente quebrada.
Mantenha o código dos casos de uso a medida em que a API evolui. Isso não apenas irá protegê-lo de surpresas desagradáveis, como também o código resultante servirá como exemplo de uso da API, a base para tutoriais e testes.
Código de exemplo deve ser exemplar. Se uma API é amplamente usada, seus exemplos serão os arquétipos de milhares de programas. Qualquer erro vai voltar para assombrá-lo com intensidade mil vezes maior.
Você não pode agradar todo mundo então tente desagradar a todos por igual. A maioria das APIs apresentam excesso de restrições.
Espere por erros de design de API causados por falhas de imaginação. Você não pode querer imaginar tudo o que todos vão fazer com a API, ou como ela vai interagir com cada parte do sistema.
Design de API não é uma atividade solitária. Mostre seu design para a maior quantidade de pessoas que você puder, e ouça o feedback delas seriamente. Possibilidades que escapam da sua imaginação podem ser claras para os outros.
Evite limites fixos de entradas. Eles limitam a utilidade e aceleram a obsolescência.
Nomes são importantes. Se esforce para ter inteligibilidade, consistência e simetria. Toda API é uma pequena linguagem e as pessoas precisam aprender a lê-la e escrevê-la. Se você acerta na API, o código será lido como prosa.
Se está disfícil achar bons nomes, volte para a prancheta. Não tenha medo de dividir ou fundir uma API, ou embutí-la em um cenário mais genérico. Se os nomes começam a entrar no lugar, você está no caminho certo.
Quando em dúvida, deixe de fora. Se há um teorema fundamental sobre design de APIs, é esse. Ele se aplica igualmente a funcionalidades, classes, metódos e parâmetros. Cada faceta de uma API deveria ser tão pequena quanto possível, mas não menor que isso. Você sempre pode adicionar coisas depois, mas você não pode retirá-las. Minimizar o peso conceitual é mais impotante que a contagem de classes ou métodos.
Deixe as APIs livres de detalhes de implementação. Eles confundem os usuários e inibem a flexibilidade para evoluir. Nem sempre é óbvio o que é um detalhe de implementação: Tome cuidado com excesso de especificação.
Minimize mutabilidade. Objetos imutáveis são simples, thread-safe e podem ser compartilhados livremente.
Documentação importa. Não importa o quão boa é uma API, ela não será usada sem boa documentação. Documente todo elemento exportável da API: toda classe, metódo, campo e parâmetro.
Considere as consequências de performance das decisões de design da API, mas não distorça uma API para ter ganhos de performance. Com sorte, boas APIs tipicamente encaminham a si próprias para implementações rápidas.
Quando em Roma, faça como os romanos. APIs precisam co-existir pacificamente com a plataforma, então faça o que é costumeiro. “Transliterar†uma API de uma plataforma para a outra é quase sempre errado.
Minimize accessibilidade; quando em dúvida, deixe privado. Isso simplifica as APIs e reduz o acoplamento.
Use subclasses apenas se você puder dizer claramente que toda instância da subclasse é uma instância da superclasse. Classes expostas nunca deveriam ter subclasses apenas para reutilizar código de implementação.
Crie o design e documente para herança ou a proíba. Essa documentação tem a forma de padrões auto-utilizados: como métodos em uma classe utilizam uns aos outros. Sem isso, a criação segura de subclasses é impossível.
Não faça o cliente fazer nada que a biblioteca possa fazer. Violar essa regra leva a código bagunçado no cliente, o que é irritante e sujeito a erros.
Obedeça o princípio da surpresa mínima. Todo método deveria fazer o que causa a menor surpresa possível, dado seu nome. Se um método não faz o que os usuários acham que ele vai fazer, bugs surgirão.
Falhe rápido. O quanto antes você reportar um bug, menor será o dano causado por ele. O melhor é em tempo de compilação. Se você precisa falhar em tempo de execução, faça o mais cedo possível.
Dê acesso programático para todos os dados disponíveis em forma de string. Caso contrário, programadores serão obrigados a fazer o parse de strings, o que é doloroso. Pior, o formato de string se tornará a API de fato.
Sobrecarregue com cuidado. Se o comportamento de dois métodos difere, é melhor lhes dar nomes diferentes.
Use o tipo de dado correto para o trabalho. Por exemplo, não use string se há um tipo mais apropriado.
Use ordem de parâmetros consistente em todos os métodos. Caso contrário, programadores vão usá-los ao contrário.
Evite longas listas de parâmetros, especialmente múltiplos parâmetros consecutivos do mesmo tipo.
Evite retornar parâmetros que demandem tratamento excepcional. Clientes vão esquecer de escrever o código de casos especiais, levando a bugs. Por exemplo, retorne vetores e coleções de tamanho zero ao invés de nulos.
Lance exceções para indicar condições excepcionais. Caso contrário, os clientes serão forçados a usar exceções para o controle de fluxo normal, levando a programas que são difíceis de ler, têm bugs ou são lentos.
Lance exceções unchecked a menos que os clientes possam realmente se recuperar da falha.
Design de API é uma arte, não uma ciência. Busque beleza e acredite nos seus instintos. Não siga cegamente as heurísticas acima, mas as viole com pouca frequência e por uma boa razão.
Veja a apresentação: Como Fazer o Design de uma Boa API & Porque isso Importa
Joshua Bloch é Arquiteto Chefe de Java no Google, autor de Effective Java, Segunda Edição (Addison-Wesley, 2008), e co-autor de Java Puzzlers: Traps, Pitfalls, and Corner Cases (Addison-Wesley, 2005) e Java Concurrency in Practice. Ele foi um Distinguished Engineer na Sun Microsystems, onde liderou o design e implementação de várias funcionalidades da plataforma Java incluindo as melhorias da linguagem no JDK 5.0 e o Java Collection Framework. Ele tem um Ph.D. pela Carnegie-Mellon e graduação pela Columbia.