27 de fevereiro de 2008

Módulos ou Classes; Funções ou Métodos

"Tudo em classes", era quase sempre o que eu fazia, exceto em módulos de abstração; mas, se eu precisasse de uma única função que nem sequer recebia argumentos, eu a colocava numa classe. E por essa razão eu tinha que freqüentemente importar a classe de dentro do módulo, instanciá-la na classe em que estava usando, e eventualmente passá-la como argumento para outra classe e por aí afora.

Um certo dia eu resolvi dar uma olhada em Django, e qual minha surpresa, ele não coloca tudo em classes (A-ha!). Tanto que até a camada View (controller) consiste em funções. A partir de então percebi que o uso excessivo de classes pode atrapalhar em vez de ajudar, e reformulei as coisas. Por essa razão mudei minha regra de quando der ponha em classe para: primeiro pense em funções e se precisar transforme elas em métodos, e assim se fará uso mais adequado do suporte que Python propicia às funções: objetos de primeira classe.

Dessa forma, pensei em alguns pontos que podem indicar a desnecessidade do uso da classe. Veja-se:

# __init__ não faz nada ou o que ele faz pode ser feito fora.
É um bom indicativo de desnecessidade. É claro que é apenas um indicativo, não é regra, existem outras razões que justificariam a classe, mas pode ser que o __init__ não receba argumentos de modo que o que ele faz pode ser feito no escopo global do módulo.

# A classe não funciona como objeto.
Parece um contradição, mas não é. É sabido que em Python (quase) tudo é um objeto, mas isso quer dizer que tudo é tratado como objeto, mas não significa que tudo funciona como objeto; vezes há que os métodos da classe só te devolvem alguma coisa, mas você não faz alguma coisa com a classe, deveria ser mais um módulo que uma classe -- note que estou usando "módulo" como sinônimo de conjunto de funções, está certo que os módulos podem conter classes e funções ao mesmo tempo).

Ilustrando: imagine uma máquina de café em contrapeso com o próprio café: Na máquina a gente coloca pó de café, água, aperta um botão e ela devolve café. Essa máquina funciona como módulo: a gente alimenta com alguns argumentos e ela retorna café, ao passo que o próprio café é o objeto, porque ele pode ser manipulado como objeto: pode ser bebido, ser vendido, ser jogado fora etc. Além disso ele possui atributos como forte, fraco, doce, amargo.

# Nem sempre a própria instância precisa passar a si mesma como argumento para outra classe.
Considerando que você procura evitar singletons desnecessários, se uma instância está fornecendo "self" como argumento para algum __init__ (que não seja a super-classe), não significa que a primeira instância precisa ser uma classe, podemos usar um terceiro módulo que importará aquele conjunto de funções e passará como argumento para outro componente, assim deixamos as coisas mais simples.

É isso em IMHO.

2 comentários:

LKRaider disse...

Concordo. Na verdade, é uma regra simples: mantenha a simplicidade (KISS).

Em geral, você usa classes quando precisa criar vários objetos do mesmo tipo mas que mantenham estados diferentes.

Se quiser apenas expor funcionalidades em comum, um módulo é mais simples e adequado.

Eduardo Willians Bandeira de Melo disse...

Valeu Ikraider pelo comentário, só agora (um mês depois) é que fui ver.