Bem, já disse em outros posts que o código "fala", em outras palavras, um código bem escrito faz os outros programadores entenderem a intenção.
Também sou adepto do ditado que diz: "Sempre que for adicionar um comentário, pense: 'Como eu posso escrever este código para que este comentário não seja necessário?'".
Mas o que estou falando neste tópico é mais do que apenas inserir comentários, digo usar o método de documentação de código disponível na linguagem na hora de criar uma API.
Aqui Josh Bloch fala como desenhar uma boa API e em um trecho da apresentação diz:
Documente todas classes, interfaces, métodos ...
Também diz:
Reuso é algo mais fácil de falar do que fazer. Fazer requer bom design e muito boa documentação.
Cada linguagem fornece um meio de documentar as classes, interfaces e métodos, no Java tem o JavaDoc no .NET tem o XML Documentation e por aí vai.
No caso do Java, os profissionais que desenvolvem na linguagem já devem ter percebido quando usam um IDE com code completion/assist, como o Eclipse ou NetBens, que aparece a descrição dos métodos, exceções e parâmetros na janela de contexto, sempre que uma classe ou método é autocompletado ou o codeassist é invocado, como na ilustração:
Mãos a obra no Javadoc então
Vou ilustrar com uma aplicação baseada no modelo Transaction Script, onde disponibilizo uma classe de serviço com uma interface para operações de Crédito e classes de dados que representam o retorno desse serviço, ok?
Então eu tenho uma classe de dados que representa uma Analise de Crédito, como abaixo:
public class AnaliseCredito {
// .. dados sobre a análise de crédito
}
A primeira documentação é sobre a classe, que irá expor detalhes importantes acerca do que as instâncias desta classe irão representar.
Todo início de documentação no padrão Javadoc inicia com /**, seguido de um * por linha e termina com */, como abaixo:
/**
*
* @author Márcio Torres
*/
public class AnaliseCredito {
}
Outras informações pertinentes podem ser a versão e a qual a versão mínima, como segue:
/**
*
* @author Márcio Torres
* @version 1.0, 09/23/10
* @since 1.5
*/
De acordo com a Oracle a versão deve usar o formato acima, mas obviamente cada equipe pode chegar em um consenso para isto, eu por exemplo gosto do formato timestamp (2010-09-23 13:55:52).
Ainda falta explicar a classe, como segue:
/**
* Representa o resultado de uma Análise de Crédito com detalhes importantes
* acerca de um determinado cliente e o risco a ele associado no que diz
* respeito a empréstimos e financiamentos.
*
* @author Márcio Torres
* @version 1.0, 09/23/10
* @since 1.5
*/
public class AnaliseCredito { ...
Tente sempre respeitar as 80 colunas para escrever esta descrição. Ainda é possível destacar certos trechos, como a tag tt que representa um trecho de código, i, strong, e outras tags HTML. Também é possível indicar a consulta para classes relacionadas através do @see, como abaixo:
/**
* Representa o resultado de uma Análise de Crédito com detalhes importantes
* acerca de um determinado cliente e o risco a ele associado no que diz
* respeito a empréstimos e financiamentos, representados pelas
* classes Emprestimo e Financiamento
*
* Para analisar o risco use o método {@link #getRisco()}
*
* @author Márcio Torres
* @version 2010-09-23 13:40:25
* @see Financiamento
* @see Emprestimo
* @since 1.5
*/
public class AnaliseCredito { ...
Como no código acima, ainda é possível estabelecer links para métodos importantes da classe. Embora existam outros detalhes, os que exemplifiquei acima já cobrem o básico, mais detalhes aqui: http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html
Documentar as interfaces e classes é só o começo, é necessário comentar os métodos. Neste caso, é comum nas interfaces esclarecer detalhes do contrato, sendo normalmente mais detalhado, e que também de algum modo dê informações a um programador que venha a implementá-la, como segue o exemplo abaixo de uma interface e uma implementação simples:
public interface CreditoService {
AnaliseCredito analisaCredito(String cpf);
}
public class CreditoServiceImpl implements CreditoService {
@Override
public AnaliseCredito analisaCredito(String cpf) {
if (cpf == null) {
throw new NullPointerException("O CPF não foi informado (nulo)");
}
if (cpf.length() < 11) {
throw new IllegalArgumentException("O CPF não está completo, use apenas números e sem formato, ex: 11122233344");
}
if (cpf.length() > 11) {
throw new IllegalArgumentException("O CPF tem caracteres a mais, use apenas números e sem formato, ex: 11122233344");
}
// Verifica o Crédito através de uma integração
// pode lançar WebServiceException
// webService.checkCredit(cpf) ...
AnaliseCredito analise = new AnaliseCredito();
return analise;
}
}
Documentando a interface:
/**
* Contrato para as operações relacionadas a <strong>Crédito</strong> e
* operações financeiras.
*
* @author Márcio Torres
* @version 2010-09-23 14:13:10
* @see AnaliseCredito
* @since 1.5
*/
public interface CreditoService {
/**
* Dado um CPF reúne informações pertinentes e traz a resposta na forma
* de uma instância de {@link AnaliseCredito}.
*
* Para obter uma análise com mais dados use
* {@link #analiseCompletaCredito(String)}
*
* @param cpf
* O número do CPF que será utilizado para recolher as informações
* sendo apenas número e sem formato, por exemplo: <tt>11122233344</tt>
* @return
* Uma análise de crédito representada pela classe
* {@link AnaliseCredito}.
* @throws NullPointerException
* Se for passado um parâmetro nulo
* @throws IllegalArgumentException
* Se o CPF não tiver 11 números ou for inválido
*/
AnaliseCredito analisaCredito(String cpf);
}
No exemplo foi documentada a interface e o método analisaCredito. Na documentação do método é explicada a pré-condição, pós-condição e efeitos colaterais, bem como o formato do parâmetro esperado e as exceções que podem ser lançadas. A classe de implementação pode ser mais espartana, mas ainda tem que documentar detalhes da implementação, como exceções novas. Segue exemplo:
/**
* Executa operações relacionadas a <strong>Crédito</strong> e financeiras
*
* @author Márcio Torres
* @version 2010-09-23 14:24:20
* @see AnaliseCredito
* @since 1.5
*/
public class CreditoServiceImpl implements CreditoService {
/**
* Dado um CPF reúne informações pertinentes devolvendo
* uma Análise de Crédito {@link AnaliseCredito}.
*
* @param cpf
* O número do CPF que será utilizado para recolher as informações,
* por exemplo: <tt>11122233344</tt>
* @return
* Uma análise de crédito
* @throws NullPointerException
* Se for passado um parâmetro nulo
* @throws IllegalArgumentException
* Se o CPF for inválido
* @throws WebServiceException
* Se houver um problema de comunicação com o subsitema de integração
*/
@Override
public AnaliseCredito analisaCredito(String cpf) {
// implementação
}
}
Resultados
O resultado da documentação, no IDE Eclipse por exemplo, é o que pode ser visto abaixo:
Ao completar o método fica assim:
E pelo Eclipse é possível exportar como HTML através de File -> Export -> Javadoc. Após concluir, se todas as opções ficarem em default, aparecerá um diretório doc no projeto e os HTML's. Basta usar um Open With -> Web Browser, e mais tarde disponibilizar a documentação em um lugar comum para a equipe.
Veja abaixo como ficou:
Outras linguagens
Outras linguagens disponibilizam o seu meio de documentação, PHP, ActionScript, C#, esta última posso dar um exemplo pois tenho mais contato:
C# com XML Documentation:
namespace Business.Services {
/// <summary>
/// Executa as operações relacionadas a crédito e finanças.
/// <summary>
/// <remarks>
/// A partir desta classe são disponibilizados ...
/// </remarks>
public class CreditoServiceImpl : CreditoService {
/// <summary>
/// Checa o crédito dado um CPF ...
/// </summary>
/// <param name="cpf">
/// CPF para consultar ....
/// </param>
/// <seealso cref="AnaliseCompletaCredito">
/// Veja o método AnaliseCompletaCredito
/// </seealso>
/// <exception cref="System.ArgumentException">
/// Lançada quando o CPF não é válido
/// </exception>
/// <exception cref="System.ArgumentNullException">
/// Lançada quando o CPF não é nulo
/// </exception>
public AnaliseCredito AnaliseCredito(String cpf) {
...
Acho que é isso. A implementação no exemplo não foi perfeita, mas penso ter coberto o objetivo de apresentar a documentação e espero ter ficado entendido a necessidade da mesma.
Abraço, até a próxima.