Antes de mais, vamos definir uma ou duas regras de vocabulário para o artigo:
- Static - Vou referir-me a esta palavra reservada como a sua tradução literal "Estático". Exemplo: classe estática, método estático...
- Instance Members - Esta é uma tradução pobre, eu sei, mas refere-se a membros que apenas estão acessíveis utilizando uma instância de uma determinada classe. Chamar-lhes-ei de membros de instância
- Isolation Frameworks - Refiro-me a frameworks que ajudam a criar mocks, fakes e stubs para unit testing. Irei traduzir como Frameworks de Isolamento
Em C# 2.0 foi adicionada uma nova funcionalidade denominada por classes e membros estáticos. Quando digo membros refiro-me a métodos, propriedades, eventos, etc. Eu tenho algumas opiniões sobre esta palavra reservada que irei partilhar ao longo do artigo, mas vamos começar por olhar para o que podemos fazer com esta feature. Vejamos este código
void Main()
{
Person p = new Person();
p.Name = "test";
Person.NumberOfBrains = 1;
}
public class Person
{
private static int _NumberOfBrains = 0;
private string _Name = null;
public static int NumberOfBrains
{
get
{
return _NumberOfBrains;
}
set
{
_NumberOfBrains = value;
}
}
public string Name
{
get
{
return _Name;
}
set
{
_Name = value ;
}
}
}
Acima estão definidas duas propriedades, sendo que uma é estática. No método Main criamos uma instância da classe Person e atribuímos um valor à propriedade da instância (Name). Olhemos agora para a parte do código que menciona "Person.NumberOfBrains = 1". Esta é a propriedade estática que criámos na classe. Reparem como o acesso é feito ao nível da classe em vez de ao nível da instância. Esta é uma diferença que pode parecer subtil, mas significa que apenas vai existir um valor na propriedade NumberOfBrains, independentemente das instâncias da classe Person criadas, mas existirá sempre um valor diferente na propriedade Name que pode variar por cada instância. Resumindo, membros estáticos são utilizados quando queremos que um determinado membro esteja acessível independentemente de haver ou não uma instância de uma classe.
Como é claro o exemplo acima não é o melhor, mas dá para perceber a ideia. Ao utilizarmos esta feature temos que ter algumas regras em mente:
- Não é possível aceder a membros de instância através de métodos estáticos. Na minha classe Person eu não poderia aceder à propriedade Name dentro de um método estático, por exemplo. Para isso teria de passar a instância por parâmetro.
public class Person
{
public string Name; //Esta variável não é static
private static void SetName() //Este método é static
{
Name = "asd"; //Não podemos aceder a um membro não static sem uma instância!
}
}
- Não é possível ter membros de instância em classes estáticas.
public static class Person
{
public string Name; //Não é possível definir este membro numa classe static
}
- Não é possível instanciar uma classe estática. O que é que estaríamos a instanciar exatamente?
- Ao utilizarmos classes estáticas perdemos alguns conceitos OO, tais como herança e composição (não podemos implementar interfaces).
Esta última é uma das razões pelas quais eu não gosto de escrever o meu código estático e tento sempre reduzir ao máximo os sítios onde utilizo. Alguns dos argumentos são:
- Código estático não é facilmente testável e substituível por mocks. A maioria das frameworks de isolamento (excepto algumas super-frameworks) utilizam herança para criar implementações próprias chamadas mocks e stubs que facilitam a testabilidade do código. Se estivermos a utilizar uma classe estática, não podemos utilizar estas frameworks e não podemos isolar partes do nosso código que utilizam essa classe estática.
- Classes estáticas não permitem boas formas de tornar o código extensível. Não é possível herdar de uma classe estática e não podemos obviamente fazer override a métodos/propriedades estáticas. Isto destrói completamente a extensibilidade da classe.
- Já vi desculpas como "desta forma não precisamos de instanciar um objecto". Para mim isto não faz sentido só por si. Claro que não temos que instanciar, mas o que ganhamos disso? Apenas em casos muito específicos é que eu não quereria utilizar uma instância de um objecto e perder todas as vantagens do polimorfismo e da herança.
- Classes e membros estáticos tornam-se um íman de mau código quando cometemos o erro de os deixar crescer ao ponto de passarem a definir regras de negócio. Antes de darmos por isso temos que passar uma miríade de argumentos ou criar um DTO para manter a lista de argumentos razoável.
Como trabalho diariamente com código muito baseado em classes estáticas, falo por experiência própria neste assunto. Já vi toda uma camada de acesso a dados baseada em classes estáticas, e isto não é algo do qual me orgulho. Claro que testá-la era um pesadelo, e apenas possível manualmente de forma razoável, pois era impossível criar mocks de dependências estáticas.
À parte disto penso que há algumas situações bastante válidas onde código estático é extremamente útil:
- Para conter métodos auxiliares que não envolvam lógica de negócio (um exemplo é a classe Convert do ..NET)
- Para manter uma lista de variáveis globalmente acessíveis ou serviços para toda a aplicação. Utilizo isto para não ter que instanciar um objecto e injectá-lo em todo o lado (sim, aquela desculpa). Claro que faço isto apenas para código muito, muito, muito simples. Normalmente, esta classe estática global apenas contém propriedades (geralmente interfaces) para não perder testabilidade.
- Métodos estáticos têm uma grande importância em C# com a introdução dos Extension Methods. Veremos isso mais tarde.
- Para conter métodos que não fazem grande sentido numa classe específica.
A minha abordagem é: tentar sempre manter o comportamento e a lógica da aplicação longe de membros estáticos. Porque estaríamos a utilizar uma linguagem OO como o C# se não utilizamos as suas capacidades OO? Claro que haverão sempre situações em que usar membros estáticos é melhor ou mais fácil, por isso é uma óptima adição na linguagem!
Continuem a programar!
Comments
Post a Comment