26 de set. de 2010

Documentar faz bem

Um recurso que às vezes não é aproveitado pelos desenvolvedores é a documentação.

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.