Google Analytics

terça-feira, 7 de janeiro de 2014

Mais sobre Qualidade (sistemas de)

Sou eu que estou fora de contexto e errado ou estou vendo algo que faz sentido?

Muita gente, talvez até a maioria aqui no Brasil, vê os sistemas de gestão de qualidade como um fim em si mesmos e não como meios para se obter software de qualidade.

Não deixo de me surpreender com este foco que vejo sendo dado para sistemas de gestão de qualidade tanto em empresas quanto em publicações da área. Parece que pouquíssimos veem estes sistemas como técnicas para obter de forma (mais ou menos) consistente um software com baixos índices de erros, e com custo e prazo aceitáveis, que são as dimensões básicas de qualidade.

Eu vejo os sistemas de qualidade por esta ótica, de ferramenta e não de fim em si. Será que estou errado?

quarta-feira, 4 de dezembro de 2013

Factory / Código sem utilidade

Recentemente, em um treinamento sobre design patterns, após apresentar o  factory method, pedi ao pessoal que fizesse um programa que, usando uma hierarquia de classes pré-existente que definia uma abstração para envio de arquivos, perguntasse ao usuário qual tipo de envio ele queria usar (entre FTP ou SMB) e enviasse um arquivo fictício usando o objeto de envio correto.

Para clarear a coisas, a abstração de envio e a hierarquia de classes correspondente era algo do tipo:

class FileSender
{
public:
  virtual sendFile(string filename, string destination) = 0;
};

class FTPFileSender: public FileSender
{
public:
  sendFile(string filename, string destination) {...}
};

class SMBFileSender: public FileSender
{
public:
  sendFile(string filename, string destination) {...}
};


Claro, a ideia era que usassem um factory method para a criação dos objetos. E todos acertaram esta parte. Todos implementaram um método FileSender::create, estático que fazia algum return new, criando objetos das classes filhas FTPFileSender ou SMBFileSender e retornando um FileSender* apontando para o objeto criado.

Até aqui 100%. O problema é que a maioria fez o seguinte:

typedef enum {FTP, SMB} FileSenderType;

FileSender::create(FileSenderType type)
{
   switch(type)
  {
    case FTP: 
      return new FTPFileSender;
    case SMB: 
      return new SMBFileSender;
    default:
      //Log error and abort.
  }
}

void main()
{
  cout << "SMB ou FTP?" << endl;
  FileSenderType myType;
  // Alguma lógica para ler resposta do usuário e guardar em myType...

  FileSender* fileSender = FileSender::create(myType);
  fileSender->send(.....);
}

Você faria exatamente do mesmo jeito?

Ah, então me acompanhe...

Me descreva o que a "factory" está fazendo e o que ela está poupando de trabalho para seu usuário (a main, no caso). Com um pouco de cuidado você vai concluir que ela  não poupa trabalho nenhum da main. Na verdade, a factory não está fazendo NADA. Ela é código inútil, pois não engloba nenhuma abstração, não poupa nenhum trabalho da camada acima. Pense o que aconteceria se você colocasse os news direto na main; haveria alguma desvantagem? Obviamente não. Por isso a "factory" acima implementada não serve para nada.

Não está convencido? Então imagine que agora queremos adicionar a possibilidade de envio de arquivos por SSH. Isto será feito criando uma nova classe SSHFileSender. Mas, com esta adição, vamos ter de mexer na main (código usuário), certo? Pois é. Agora imagine um caso mais real... Em vez de um ponto só de uso, um programa real pode ter muitos pontos de uso espalhados. E, para adicionar uma funcionalidade específica de FileSenders, vamos ter de sair procurando pelo código inteiro... Ruim, não? Pois é. E pensar que o pattern factory foi criado justamente para evitar isto...

Dá para melhorar? Dá sim. Vamos atentar para o fato de que a "factory" implementada não faz nada e é código inútil. Vamos tentar dar alguma utilidade para esta nossa FileSender::create(). Que tal se ela se responsabilizasse por perguntar ao usuário qual FileSender usar? Ou seja, colocar dentro dela o código que o pessoal colocou na main:

FileSender::create()
{
  typedef enum {FTP, SMB} FileSenderType;

  cout << "SMB ou FTP?" << endl;
  FileSenderType chosenType;
  // Alguma lógica para ler resposta do usuário e guardar em chosenType...

  switch(chosenType)
  {
    case FTP: 
      return new FTPFileSender;
    case SMB: 
      return new SMBFileSender;
    default:
      //Log error and abort.
  }
}

void main()
{
  FileSender* fileSender = FileSender::create();
  fileSender->send(.....);
}

Você consegue ver que implementando desta forma a factory se tornou realmente útil? Você consegue ver que, agora, para adicionar um novo tipo de FileSender, eu só preciso mexer em entidades que estão dentro da hierarquia de classes FileSender? Você consegue perceber a vantagem disto?


Bom, por hoje era isto.

Período sem publicações

Há um longo tempo desde minha última publicação neste blog.

A questão é que eu estive envolvido em muitos assuntos nos últimos tempos, sobrando pouco tempo para conseguir elaborar posts com qualidade.

Entre estes diversos assuntos, um é muito especial e tão querido que dispendo todos meus momentos livres cuidando dele. E isto é muito mais divertido do que escrever sobre programação: Meu filhão João Paulo, que está com 2 anos.

Mas ontem terminei um artigo que iniciei há tempos e vou publicá-lo daqui a pouco. Fiquei feliz de ver que, mesmo sem postar novos artigos, o blog continua sendo bastante acessado.

quarta-feira, 16 de maio de 2012

Árvore de fontes

Neste post vou falar como eu costumo organizar os arquivos-fonte de meus projetos. A divisão que eu uso obviamente não é a única adequada e nem a melhor, mas tem funcionado relativamente bem. As características que foram buscadas nesta formatação são:
  • Facilidade de localização do material.
  • Formato estruturado e hierárquico.
  • Manter todo o material de entrada necessário para o trabalho versionado e baixável com um comando único.
  • Possibilidade de usar integração contínua e builds CRISP.
 Vamos então à estrutura.

project-x
├── code
│   ├── build
│   │   ├── output (não guardado no sistema de versionamento)
│   ├── internalDocs
│   │   └── TestPlans
│   ├── skels
│   ├── thirdparty
│   │   ├── bin
│   │   ├── dll
│   │   ├── include
│   │   └── lib
│   └── userDeliverables
│       ├── part1
│       ├── part2
│       ├── installers
│       └── userDocs
├── continuous-integration
└── thirdparty-originals

No primeiro nível, temos a divisão em code, continuous-integration e thirdparty-originals. Code vai conter o grosso do trabalho, e será descrito por último.

Em thirdparty-originals,  coloco todos os arquivos de peças de software de terceiros que serão usados no projeto. Importante: eu os coloco do jeito que eles vêm do terceiro, sem nenhum processamento. Todo o processamento que estes arquivos requeiram eu faço através de scripts para garantir a repetibilidade do processo e facilitar no caso de upgrade de uma peça. Os scripts de processamento criados para isto vão ficar em um lugar abaixo do dir code, que será descrito mais para a frente.


Em continuous-integration, coloco os arquivos usados para fazer integração contínua do projeto. Por exemplo, se vai ser usado cruise-control, aqui ficarão um cruise-control-config.xml e um delegating.xml.


O diretório code está organizado do modo abaixo descrito:

No diretório build, coloco os scripts de build que permitem buildar o sistema em linha de comando. Sempre procuro ter um script que permita, rodando um único comando, gerar o pacote pronto para envio para clientes. Gosto que este script crie um sub-dir chamado output e coloque seus produtos ali. Notem que este script de build master deve gerar inclusive a documentação de produto e empacotá-la do jeito necessário para a entrega (zip contendo os documentos e o instalador, por exemplo).

No diretório  internalDocs coloco a documentação de projeto. Por exemplo, diagramas de projeto, instruções de trabalho para programadores, user stories, planos de teste, etc. Vou usando sub-dirs para agrupar coisas relacionadas. Por exemplo, se tenho mais de um plano de teste, crio um sub-dir chamado testPlans, se tenho vários arquivos de user stories, crio um chamado userStories.

Em skels, coloco os arquivos de esqueleto que serão usados no projeto. É interessante que a maioria das pessoas que conheço guardam os esqueletos no armário (ok, normalmente é na pasta da qualidade ou no diretório de políticas da empresa) e não junto de onde eles são usados e, guardando no armário, não dão oportunidade de customização por projeto e muita gente acaba não usando-os por estarem longe. Eu gosto de meus esqueletos evoluindo, versionados e sempre à mão para serem usados de modo efetivo. A propósito, esqueletos são aqueles arquivos modelo que ajudam a ter uma homogeneidade visual no código e também ajudam a gente a se lembrar de detalhes como esconder o copy ctor de classes. Se você quer uns exemplos de esqueletos, de uma olhada aqui.

Em thirdparty ficam os arquivos de peças de software de terceiros, cujos originais estão em thirdparty-originals, no formato em que eles precisam estar para serem usados no projeto. Aqui ficam também os scripts necessários para transformar o que temos em thirdparty-originals para o formato necessário. Um exemplo disto é uma biblioteca de terceiros. Digamos, por exemplo, que eu use a OpenSSL em Windows. O targz dos fontes dela estaria em thirdparty-originals e um script chamado buildOpenSSL, guardado em thirdparty compilaria as libs e as copiaria para thirdparty/dll e os includes públicos para thirdparty/include.

Em UserDeliverables coloco os fontes das várias partes do projeto (ou da única parte, se for o caso) e também a documentação de produto (manual do usuário, readme, history, etc).





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...