No âmbito da minha dissertação tenho trabalhado com uma biblioteca open source que, infelizmente, está mal documentada. E eu sou grande apologista de projetos open source, mas deixemos isso para um outro commit.
Tal como as metodologias agile nos ensinaram, existe sempre um trade-off entre working software e documented software. Ainda assim, há cuidados básicos que devemos ter para deixar o código tão legível e claro quanto possível. Neste commit foquemo-nos nas assinaturas das funções.
Imaginemos a função pause
, que coloca a thread onde é executada em pausa. Se essa função estiver definida como
existirão, naturalmente, dúvidas sobre qual a unidade de tempo a ser passada como argumento. Segundos? Milisegundos? Minutos?
Apesar de já ser mais aceitável termos algo como void pause(int timeToPauseInSeconds);
, porque não declará-la como
ou com os tipos std::chrono::duration
1 introduzidos no C++11, que pretendem terminar com esta ambiguidade:
Agora imaginemos que queremos aumetar a granularidade das unidades, de forma a conseguir pausar ao nível do nanosegundo. Neste caso, passaríamos a ter
Agora imaginem que precisamos de outra função para parar ao nível do microsegundo… Ok, acho que já entenderam onde quero chegar. Obviamente podemos escrever esta função de uma forma mais genérica
que, já agora, utiliza um template da biblioteca de suporte a threads também introduzida no C++11. Esta combinação permite interoperabilidade com o tipo de granularidade de tempo que deserjarmos.
Reparem como introduzi outra alteração no código que ainda não discutimos. Decidi passar o valor por referência e, como
tal, utilizei a keyword const
como garantia de que o valor não irá ser alterado. Isto não só me dá segurança de que
irei obter um erro em compile-time se a regra for violada, como também serve de boa documentação. De que forma? Sigam
o raciocínio com um novo exemplo.
Imaginemos a função countToTen
, que recebe um inteiro por referência e conta, a partir desse valor, até 10:
Sem vermos a documentação (se existir), ou o código, não temos garantia nenhuma de que o valor de number
se mantém
inalterado, como na implementação que se segue
Assim como também podemos ter uma implementação que não muda o valor da variável
Independentemente da implementação, a simples inclusão da keyword const
não só melhora a qualidade e robustez do
código, como também dá garantias a futuros utilizadores da vossa função que o valor variável que passam como argumento
não irá ser alterado. Assim, em operações que necessitem de receber um valor por referência, a
keyword const
é vossa amiga!
E sim, eu sei que a questão, neste caso, fica resolvida se passarmos por valor, mas imaginemos que o gasto computacional para copiar um inteiro é relevante o suficiente para passarmos apenas uma referência.
-
http://en.cppreference.com/w/cpp/chrono ↩