Google Analytics

segunda-feira, 7 de março de 2011

Orientação a objetos

Prezados.

O preceito mais básico da Orientação a Objetos é ter dados e operações dentro da mesma unidade lógica (no caso, a classe). Isto também se diz da seguinte forma: Os objetos devem ser responsáveis por si próprios; eles é que devem realizar as operações sobre seus dados internos.

Outro preceito importante é o de encapsulamento de dados ou information hiding. É até anterior à OO, importante também em outros paradigmas de programação. Trata-se de não expor detalhes de como uma entidade é implementada; expor apenas a interface da entidade. Em OO, isto significa, ter poucos (idealmente, nenhum) dados públicos (no sentido semântico de públicos). Um excelente livro sobre programação que eu li, e agora me escapa qual, dizia a este respeito que devemos seguir o conselho ouvido na infância: "Don't show your private parts."

Perco as contas de quantas vezes vejo isto tudo ser deixado de lado. Por exemplo quando fazem algo assim:


class Horario
{
  unsigned hora;
  unsigned minuto;
public:
  unsigned getHora();
  unsigned getMinuto();
  void setHora(unsigned hora);
  void setMinuto(unsigned minuto);
};

main()
{
  Horario t0, t1, t2, t3;
  t0.setHora = 10;
  t0.setMinuto = 10;

  // um pouco mais de código, inicializando tn e fazendo sabe-se mais o que....

  // então imprime alguns t:
  cout << t2.getHora() << ':' << t2.getMinuto() << endl;
  cout << t3.getHora() << ':' << t3.getMinuto() << endl;
}


estão jogando no lixo os preceitos mencionados acima.

O information hiding foi pro vinagre, pois embora os membros não sejam declarados como públicos, semanticamente eles são totalmente públicos: usuários da classe podem fazer o que bem entenderem com eles... Os getXXX e setXXX geram o problema.

Além disso, o preceito básico da OO foi pro espaço também. Notem que as operações (inicialização e "impressão") sobre os objetos estão sendo feitas FORA do objeto (fora da classe).

Melhor seria algo como:


class Horario
{
  unsigned hora;
  unsigned minuto;
public:
  Horario(unsigned h, unsigned m): hora(h), minuto(m) {};
  string paraString()
   {/*converte para string no formato hh:mm, possivelmente usando ostringstream*/};
  //Eventualmente outras operações com Horarios, mas nada de getXXX ou setXXX
 };

main()
{
  Horario t0(10,10);
  Horario t2(5,11);
  Horario t3(23,55);

  // um pouco mais de código, inicializando tn e fazendo sabe-se mais o que....
  // então imprime alguns t:
  cout << t2.paraString() << endl;
  cout << t3.paraString() << endl;
}
Ou, para este caso de "impressão", usando um operador de inserção em stream:
 
class Horario
{
  friend ostream& operator<<(ostream& s, const Horario& h);
  unsigned hora;
  unsigned minuto;
public: 
  Horario(unsigned h, unsigned m): hora(h), minuto(m) {};
  //Eventualmente outras operações com Horarios, mas nada de getXXX ou setXXX
 };

ostream& operator<<(ostream& s, const Horario& h)
{
  s <<  h.hora << ':' << h.minuto;
  return s;
}

main()
{
  Horario t0(10,10);
  Horario t2(5,11);
  Horario t3(23,55);

  // um pouco mais de código, inicializando tn e fazendo sabe-se mais o que....

  // então imprime alguns t:
  cout << t2 << endl;
  cout << t3 << endl;
}

IMPORTANTE!!!!!! Nesta implementação acima, a classe e o operator<< devem estar no mesmo módulo. Ou seja, as declarações de ambas no mesmo .h (horario.h, por exemplo) e as implementações de ambas no mesmo .cpp (horario.cpp, por exemplo). Notem que quebramos um pouco do information hiding nesta implementação, já que o operator<< precisa acessar os dados privados da classe. Quer dizer, a classe não mostra as suas "partes privadas" para todo mundo, mas mostra para os "amiguinhos". Não é o ideal e não me agrada, mas até pode ser aceitável em algumas situações, desde que classe e operador tenham uma "união estável" bem forte e a classe tenha poucos "amiguinhos" (entidades friend).


Eu prefiro uma implementação que inclui o encapsulamento da paraString() e a conveniência do operador<<: Implemento o operator<< chamando o paraString(). Neste caso, a declaração de friend fica desnecessária.