Google Analytics

quinta-feira, 26 de abril de 2012

Esqueletos

Eu gosto de usar esqueletos, ou seja, aqueles arquivos de modelo. Eles me ajudam a lembrar de coisas que normalmente precisamos e também deixam o código com uma cara homogênea.

Mas eu não gosto de esqueletos mortos, daqueles que estão estáticos e não podem ser mudados, ou que são iguais para todos os projetos. Gosto de esqueletos vivos, que evoluem, sendo customizados para cada projeto conforme vão sendo usados e gosto também que eles estejam versionados no sistema de versionamento DO PROJETO EM QUESTÃO.

Costumo usar os seguintes esqueletos:

header:
//*****************************************************************************
// Version/Revision log :
// ====================
//$Log: $
//
//*****************************************************************************

#ifndef TODO_NAME_H_INCLUDED
#define TODO_NAME_H_INCLUDED

//################################# Includes ##################################


//################################## Types ####################################


//################################ Constants ##################################


//############################## Public Variables #############################


//############################# ForwardDeclarations ###########################


//################################# Classes ###################################


//####################### Public Function Declarations ########################


#endif // TODO_NAME_H_INCLUDED

//*************************    E n d   F i l e    *****************************



cpp:

//*****************************************************************************
// Version/Revision log :
// ====================
//$Log: $
//
//*****************************************************************************

//################################# Includes ##################################
#include "TODO_NAME.h"


//################################## Types ####################################


//######################### Private Classes Declaration  ######################


//################################ Constants ##################################


//############################# Private Variables #############################


//############################## Public Variables #############################


//############################# Private Functions #############################


//###################### Private Classes Implementation #######################


//###################### Public Classes Implementation ########################


//##################### Public Functions Implementation #######################


//-------------------------------------------------
// Unit tests
//-------------------------------------------------
#ifdef UNIT_TESTS
#include <iostream>
#include <assert.h>
using namespace std;

int main ()
{
    cout << "Unit Testing for TODO_NAME class" << endl;
    Logger::logTostdout();
       
    cout << "Testing TODO" << endl;
    {
        assert(!"No UNITTESTS implemented yet!");
    }

    cout << ">>>>>> Tests finished with no errors" << endl;
    return 0;
}

#endif

//*************************    E n d   F i l e    *****************************


declaração de classe:
//=============================================================================
// Class: TODO_NAME
// Remarks: TODO
//=============================================================================
class TODO_NAME
{
public:
    TODO_NAME() {}
    ~TODO_NAME() {}
private:
    TODO_NAME(const TODO_NAME& x);    // Hide copy ctor to avoid implicit calling
    TODO_NAME& operator=(const TODO_NAME& x);    // Hide = operator to avoid implicit calling
}; // End of class TODO_NAME



Implementação de clase:
//=============================================================================
// Class: TODO_NAME
//=============================================================================
//-----------------------------------------------------------------------------
// Method: TODO
// Remarks: TODO
//-----------------------------------------------------------------------------
void TODO_NAME::TODO(TODO)
{
    //TODO
}


Makefile:
#*****************************************************************************
# Version/Revision log :
# ====================
#$Log: $
#
#*****************************************************************************
all:
    <<TODO: Implement all target to build your items in the current dir

clean:
    <<TODO: Implement clean target that undoes what all does...

install:
    <<TODO: Implement install target that installs the itens created by all to the correct places.

uninstall:
    <<TODO: Implement clean target that undoes EVERYTHING that install does...


.PHONY: all clean install uninstall



quinta-feira, 5 de abril de 2012

Uso robusto do default em switches

Noto uma grande tendência a usarem o default do switch para economizar digitação.

Por exemplo:

O cara tem 8 possíveis valores e tem de fazer o mesmo processamento para 6 deles; então coloca 2 cases e cobre os 6 comuns com um default. 

enum {V1, V2, V3, V4, V5, V6, V7, V8} v;
...

switch (v)
{
case V1:
  processV1();
case V2:
  processV2();
 
default:
  process3to8();
}

Funciona no dia em que é implementado. Mas é um pesadelo para manutenção. No dia que alguém incluir novos valores na enumeração, o novo valor vai ser "automagicamente" considerado como devendo ter o mesmo comportamento que o V3 a V8 tinham. O que pode estar errado. Coisas que acontecem automagicamente não são boas em programação. Por isso é uma boa prática se colocar os valores explicitamente e usar o default para logar erro indicando um valor inesperado (e, de preferência, abortar o programa imediatamente):

enum {V1, V2, V3, V4, V5, V6, V7, V8} v;
...


switch (v)
{
case V1:
  processV1();

case V2:
  processV2();

case V3:
case V4:
case V5:
case V6:
case V7:
case V8:
  process3to8();
default:
  /* Logar uma mensagem indicando que um valor inesperado ocorreu na variável v, da função tal... */
/* Sempre que possível, abortar o programa nesta situação. */
}


O ponto é que o default deve ser usado para expressar: Faça isto para todos os outros valores possíveis (tanto conhecidos quanto os desconhecidos). E não é bom programar para fazer coisas com valores desconhecidos.

O mesmo fenômeno ocorre para o else em trechos do tipo:

enum {V1, V2, V3, V4} v;
...
v = getValueFromSomewhere()

if(v== V1|| v==V2)
  doSomething()
else //Tratando o V3 e V4 como genérico (isto não é bom...)
  doOtherStuff();


Melhor seria usar algo como:

if (v== V1|| v==V2)
{
  doSomething()
 }
else if (v== V3 || v == V4)
{

   doOtherStuff();
 }
else
{
  DebugLog <<"Unexpected value received  in function XXXX. Value is " << v;
  abort();
}

Era isto.

quinta-feira, 9 de fevereiro de 2012

Sugestões de leitura


Seguidamente me pedem algumas recomendações de livros. Então, montei esta lista aqui. Ela está dividida em um grupo de livros genéricos, que se aplicam a qualquer linguagem de programação, e outro específico de C++. Recomendo ler os livros mais ou menos na ordem em que aparecem listados. Sobretudo recomendo que todo mundo comece lendo o "The Pragmatic Programmer, From Journeyman to Master."

Vamos às listas:


Desenvolvimento de software em geral, independente de linguagem:

    The Pragmatic Programmer, From Journeyman to Master. David Thomas e Andrew Hunt. Este livro é um divisor de águas na carreira de um programador. Depois de lê-lo, você nunca mais será o mesmo.

    Extreme Programming Explained. Kent Beck. É fundamental conhecer os conceitos apresentados neste livro, mesmo que não se vá aplicar tudo o tempo todo (BTW, o próprio Beck diz que nem todo o tipo de projeto deve ser feito em eXtreme Programming).

    Refactoring: Improving the Design of Existing Code. Martin Fowler, Kent Beck, John Brant, William Opdyke, Don Roberts. Bastante indicado para quem trabalha com código herdado ou com equipes muito heterogêneas.
   
    The Agile Manifesto. Kent Beck et alli. http://agilemanifesto.org/. Recomendo também uma lida nas páginas adjacentes, como a de princípios. É tudo bem curto e se lê em poucos minutos.

    Pragmatic Project Automation: How to Build, Deploy, and Monitor Java Apps. Mike Clark. Embora ele use java nos exemplos, as ideias ajudam em qualquer linguagem.
   
    Agile Software Development (The Agile Software Development Series). Alistair Cockburn.

    Planning Extreme Programming. Kent Beck e Martin Fowler. Interessante para quem pretende gerenciar projetos ou ter uma participação maior na gerência de seu projeto. Mesmo que não trabalhe em projetos XP, vale a pena ler.

    Code Reading: The Open Source Perspective (Effective Software Development Series). Diomidis Spinellis. Ler código é uma habilidade extremamente importante. Apesar dos comentários depreciativos  que a gente encontra em vários sites sobre o estilo do autor, o livro vale a pena; traz ideias bem interessantes e chama a atenção para a necessidade desta habilidade. De quebra, o leitor também pode se sensibilizar e passar a tentar escrever código lembrando que outros humanos o lerão. BTW, li uma frase um dia desses que dizia algo como: "Escreva o código sempre pensando que o próximo programador a dar manutenção nele será um psicopata violento que sabe o seu endereço."

C++:

    Effective C++:50 Specific Ways to Improve Your Programs and Design. Scott Meyers. Leitura (e compreensão) obrigatória para qualquer candidato a programador C++ verdadeiro.

    More Effective C++: 35 New Ways to Improve Your Programs and Designs. Scott Meyers. Mais básico que o anterior. Talvez muita gente não aprenda nada de novo com este.

    Effective STL: 50 Specific Ways to Improve Your Use of the Standard Template Library. Scott Meyers. Leitura (e compreensão) obrigatória para qualquer candidato a programador C++ verdadeiro.

    The C++ Standard Library: A Tutorial and Reference. Nicolai M. Josuttis. Leitura obrigatória para programadores C++ médios em diante.

    The C++ Programming Language. Bjarne Stroustrup. Leitura obrigatória para programadores C++. Ponto a mais para que pronunciar corretamente o nome do autor ;-).

    Effective C++:50 Specific Ways to Improve Your Programs and Design. Scott Meyers. Não foi engano... Está duplicado de propósito. Quando você tiver chegado aqui, deve reler este livro. Ele é pequeno, rápido para reler. E provavelmente, na primeira vez que você leu, você não entendeu alguma coisa...



  

quarta-feira, 19 de outubro de 2011

Código autodocumentado

Prezados, código auto-documentado é diferente de colocar comentários no código explicando o que ele faz ou de colocar documentação doxygen ou afins.

Código auto-documentado é código escrito de forma clara, com nomes descritivos nas entidades (variáveis, funções, constantes, etc) e com uma estruturação lógica que facilita o entendimento. Levando a um exemplo extremo, a seção abaixo não é código auto-documentado:

/* Function f(string s1, string s2)
This function prints a name on the screen, formatting as "surname, name". s1 is the name and s2 is the surname.
*/
f(string s1, string s2)
{
   ...
}

Nem que o comentário fosse no padrão doxygen, isto não seria código auto-documentado!

Agora um exemplo de auto-documentado:

//----------------------------------------------
// This function prints a name on the screen, formatting 
// as "surname, name".
printAsSurnameCommaName(string name, string surname)
{
...
}


domingo, 14 de agosto de 2011

Como documentar um refix no documento de change log de um software

Recentemente um colega me consultou sobre como documentar, no changelog que vai para o usuário, a correção de um bug que foi reaberto. Abaixo eu transcrevo a conversa (com autorização - agradecimentos ao Mateus pela autorização) pois outras pessoas também podem ter a mesma dúvida.

(13:26:31) Mateus: se eu corrigo um bug e coloco no changelog:
(13:26:39) Mateus: fix bug XXX....
(13:26:47) Mateus: mas alguém vai lá e reabre o dito cujo.
(13:26:59) Mateus: aí eu corrijo e libero outra versão, com o novo fix.
(13:27:04) Mateus: como coloco no changelog?
(13:27:07) Mateus: Fix bug xxx
(13:27:12) Mateus: ou Refix bug xxx ?
(13:27:25) lg.moreira: changelog do source control ou do bug tracking?
(13:27:33) Mateus: do pacote. [N.R. - Quer dizer: no documento que vai para o usuário.]
(13:27:40) lg.moreira: ok.
(13:27:41) Mateus: o arquivo changelog que vai pro usuário.
(13:28:06) lg.moreira: Fix of bug xxxx after its reopening by Fulano.
(13:28:50) lg.moreira: Ou, se quiseres mais curto: Refix of bug xxxx: Now I yyyyyyy [descrição do que foi feito] and it should really fix the problem.
(13:29:00) Mateus: hmmm
(13:29:09) Mateus: esse segundo me agrada mais porque to colocando o título do bug também na linha.
(13:29:22) lg.moreira: O importante é te colocares no lugar de um usuário que lê o changelog.
(13:29:50) Mateus: pois é
(13:29:55) lg.moreira: Se tu colocas só refix, o cara vai ficar perguntando WTH he means with refix. What was still broken.
(13:30:34) Mateus: acho q não preciso ser tão verboso, já que o usuário é interno e tem acesso ao bugzila
(13:30:51) Mateus: mas acho bom indicar que foi reaberto e essa é uma "re-correção"
(13:31:17) lg.moreira: No caso do usuário ter acesso ao Bugzilla, o melhor mesmo é só refix of XXXX: Now I yyyyyy.
(13:31:27) Mateus: blz.
(13:31:29) Mateus: valeu.
(13:31:41) lg.moreira: Tu colocas no bugzilla o que foi feito para corrigir?
(13:32:23) Mateus: sim(13:32:37) lg.moreira: ok. Isto é importante também.
(13:32:48) lg.moreira: Agora a cobrança do serviço de consultoria.
(13:33:16) lg.moreira: Eu gostaria de colocar este diálogo no meu blog de programação.
(13:33:29) lg.moreira: Ele exemplifica uma dúvida muito comum.
(13:33:54) lg.moreira: Estou autorizado?
(13:33:57) Mateus: sim
(13:33:59) lg.moreira: ok.
(13:34:00) lg.moreira: ;-)

terça-feira, 9 de agosto de 2011

Tornar a vida do usuário mais simples: Nossa meta

Lembre-se sempre de que programas de computador existem para facilitar a execução de um trabalho. Ou seja, para tornar a vida do usuário mais simples.

O que isto significa do ponto de vista de um programador? Significa que, ao tomar decisões de projeto, você deve pautá-las considerando o que vai tornar mais simples a vida do usuário, mesmo que isto implique em tornar a sua vida mais complicada. Resumindo: sempre optar pelo que dá menos trabalho para o usuário, mesmo que isto implique em mais trabalho para o programador.

segunda-feira, 8 de agosto de 2011

Command Line Interfaces

Taí um artigo que eu gostaria de ter escrito. Mas já o fizeram muito bem em http://www.antoarts.com/designing-command-line-interfaces/. Recomendo que leiam.

Alguns excertos:
  • "Ease of automation: Most command-line interfaces can easily be automated using scripts."
  • "Fast startup times: Most command-line interfaces beat their graphical counterparts several times at startup time"
  • "Easier to use remotely: Maybe it’s just me, but I prefer to remotely control computers via SSH over VNC"
  • "Higher efficiency: Being able to do a lot by just typing some characters instead of searching in menus increases the efficiency which you are able to do work at"
  • Try to avoid interactivity 
  • Name the program correctly
  • Follow the CLI conventions on arguments
  • "Provide --version and --help"
  • "If a program has nothing of importance to say, then be quiet"
  • "Every program should do one, and only one thing, and do it well"