Por que variáveis ​​estáticas são consideradas más?

635

Eu sou um programador Java que é novo no mundo corporativo. Recentemente desenvolvi um aplicativo usando Groovy e Java. Durante todo o código que escrevi, utilizamos um bom número de estáticas. O lote técnico sênior me pediu para reduzir o número de estáticas usadas. Eu pesquisei sobre o mesmo e descobri que muitos programadores são contra o uso de variáveis ​​estáticas.

Acho variáveis ​​estáticas mais convenientes de usar. E presumo que eles também sejam eficientes (por favor, corrija-me se estiver errado), porque se eu tivesse que fazer 10.000 chamadas para uma função dentro de uma classe, ficaria feliz em tornar o método estático e usar um método simples Class.methodCall()em vez de bagunçando a memória com 10.000 instâncias da classe, certo?

Além disso, a estática reduz as interdependências nas outras partes do código. Eles podem agir como detentores de estado perfeitos. Além disso, acho que as estáticas são amplamente implementadas em alguns idiomas, como Smalltalk e Scala . Então, por que essa opressão pela estática prevalece entre os programadores (especialmente no mundo do Java)?

PS: corrija-me se minhas suposições sobre estática estiverem erradas.

Vamsi Emani
fonte
43
Apenas para dizer, não há variáveis ​​ou métodos estáticos no Smalltalk ou Scala, exatamente porque os métodos e variáveis ​​estáticos são contrários aos princípios da POO.
Maurício Linhares
87
Pelo menos uma afirmação que você faz é bastante curiosa: "a estática reduz as interdependências nas outras partes do código". Em geral, eles reforçam as dependências. O código em que a chamada é feita está muito ligado ao código chamado. Sem abstração entre dependência direta.
Arne Deutsch
11
Boa pergunta ... mais de um Programmers.SE Perguntas tho?
WernerCD
26
Seu segundo parágrafo é sobre um assunto totalmente diferente, a saber, métodos estáticos .
Paul
8
A programação funcional também desaprova o estado global. Se você alguma vez (e deveria ) entrar no FP um dia, esteja preparado para abandonar a noção de estado global.
new123456

Respostas:

689

Variáveis ​​estáticas representam estado global. Isso é difícil de raciocinar e difícil de testar: se eu criar uma nova instância de um objeto, posso raciocinar sobre seu novo estado nos testes. Se eu usar código que está usando variáveis ​​estáticas, ele pode estar em qualquer estado - e qualquer coisa pode estar modificando-o.

Eu poderia continuar por um bom tempo, mas o conceito maior em que pensar é que quanto mais restrito o escopo de algo, mais fácil é o raciocínio. Somos bons em pensar em coisas pequenas, mas é difícil argumentar sobre o estado de um sistema de um milhão de linhas se não houver modularidade. A propósito, isso se aplica a todo tipo de coisa - não apenas às variáveis ​​estáticas.

Jon Skeet
fonte
57
Ultimamente, isso parece ser um argumento, seja com código testável ou não. É um raciocínio bastante imperfeito. O argumento deve ser 'bom design', e geralmente um bom design é testável. Mas não o contrário: "Não posso testá-lo, portanto deve ser um design ruim". Não me interpretem mal, eu concordo com o seu post em geral.
M Platvoet
144
@ Platvoet M.: eu diria que, dada a escolha entre dois projetos igualmente válidos, o testável é superior. Ser testável certamente não equivale a ser bem projetado, mas raramente me deparei com bons designs não testáveis, e acho que são suficientemente raros que não tenho problema em tornar a testabilidade um indicador geral que contribui para um bom design.
111111 Jon Skeet
9
@M Platvoet - A testabilidade afeta a manutenção e a confiabilidade, e eu consideraria esses fatores importantes na qualidade do design. Eles não são os únicos fatores, certamente, mas IMHO, o custo de qualquer código é uma combinação de ciclos de máquina, ciclos de desenvolvedor e ciclos de usuário. A testabilidade atinge dois desses três.
Justin Morgan
5
@M Platvoet - A testabilidade também tende a afetar a reutilização, uma vez que uma classe dissociada geralmente é mais fácil de reutilizar.
TrueWill 19/08/11
13
M Platvoet - Não concordo com o seu primeiro comentário aqui. Eu acho que se algo não pode ser testado, então é um design ruim; porque se não posso testá-lo, não sei se funciona. Você compraria um carro se o vendedor lhe dissesse "O design deste modelo impede que ele seja testado, então não sei se ele realmente funciona"? A testabilidade é tão crucial para o software (como para os carros), que o design competente EXIGE que ele seja incluído.
Dawood ibn Kareem
277

Não é muito orientado a objetos: Uma razão pela qual as estáticas podem ser consideradas "más" por algumas pessoas é que elas são contrárias ao paradigma orientado a objetos . Em particular, viola o princípio de que os dados são encapsulados em objetos (que podem ser estendidos, ocultação de informações etc.). As estáticas, da maneira que você as descreve, são essencialmente para usá-las como uma variável global para evitar lidar com questões como escopo. Entretanto, variáveis ​​globais são uma das características definidoras do paradigma de programação procedural ou imperativa, não uma característica do código "bom" orientado a objetos. Isso não quer dizer que o paradigma processual seja ruim, mas tenho a impressão de que seu supervisor espera que você escreva "um bom código orientado a objetos" e que realmente queira escrever "

Existem muitas dicas em Java quando você começa a usar estática que nem sempre é imediatamente óbvia. Por exemplo, se você tiver duas cópias do seu programa em execução na mesma VM, elas dividirão o valor da variável estática e interferirão no estado uma da outra? Ou o que acontece quando você estende a classe, você pode substituir o membro estático? Sua VM está ficando sem memória porque você tem números insanos de estática e essa memória não pode ser recuperada para outros objetos de instância necessários?

Vida útil do objeto: Além disso, as estáticas têm uma vida útil que corresponde ao tempo de execução inteiro do programa. Isso significa que, mesmo quando você terminar de usar sua classe, a memória de todas essas variáveis ​​estáticas não poderá ser coletada como lixo. Se, por exemplo, você tornou suas variáveis ​​não estáticas e, em sua função main (), criou uma única instância da sua classe e pediu à sua classe para executar uma função específica 10.000 vezes, uma vez que essas 10.000 chamadas foram feitas , e você excluir suas referências para a instância única, todas as suas variáveis ​​estáticas poderão ser coletadas e reutilizadas como lixo.

Impede certa reutilização: Além disso, métodos estáticos não podem ser usados ​​para implementar uma interface, portanto, métodos estáticos podem impedir que certos recursos orientados a objetos sejam utilizáveis.

Outras opções: Se a eficiência é sua principal preocupação, pode haver outras maneiras melhores de resolver o problema de velocidade do que considerar apenas a vantagem de a invocação ser geralmente mais rápida que a criação. Considere se os modificadores transitórios ou voláteis são necessários em qualquer lugar. Para preservar a capacidade de inclusão, um método pode ser marcado como final em vez de estático. Os parâmetros do método e outras variáveis ​​podem ser marcados como finais para permitir certas otimizações do compilador com base em suposições sobre o que pode alterar essas variáveis. Um objeto de instância pode ser reutilizado várias vezes, em vez de criar uma nova instância a cada vez. Pode haver opções de otimização do compilador que devem estar ativadas para o aplicativo em geral. Talvez o design deva ser configurado para que as 10.000 execuções possam ser multiencadeadas e aproveitar os núcleos de vários processadores. Se a portabilidade não for

Se, por algum motivo, você não desejar várias cópias de um objeto, o padrão de design singleton, possui vantagens sobre objetos estáticos, como segurança de thread (presumindo que seu singleton seja bem codificado), permitindo inicialização lenta, garantindo que o objeto tenha sido inicializado corretamente quando usado, subclassificação, vantagens em testar e refatorar seu código, para não mencionar, se em algum momento você mudar de idéia sobre querer apenas uma instância de um objeto, é MUITO mais fácil remover o código para evitar instâncias duplicadas do que refatorar todo o seu código de variável estática para usar variáveis ​​de instância. Já tive que fazer isso antes, não é divertido, e você acaba editando muito mais classes, o que aumenta o risco de introduzir novos bugs ... muito melhor configurar as coisas "corretamente" na primeira vez, mesmo que pareça ter suas desvantagens. Para mim, o re-trabalho necessário, se você decidir no futuro que precisa de várias cópias de algo, é provavelmente um dos motivos mais convincentes para usar a estática com a menor frequência possível. E, portanto, eu também discordo de sua afirmação de que a estática reduz as interdependências. Acho que você terminará com um código mais acoplado se você tiver muitas estáticas que podem ser acessadas diretamente, em vez de um objeto que "sabe fazer" algo "em si mesmo.

Jessica Brown
fonte
11
Gosto da sua resposta, acho que ela se concentra nas compensações certas a serem consideradas em relação à estática, e não em alguns dos argumentos errados, como simultaneidade e escopo. E +1 para singletons, a melhor pergunta realmente poderia ter sido quando usar variáveis / métodos estáticos vs singletons ...
studgeek
2
Embora o próprio singleton possa ser seguro para threads (por exemplo, usando synchronizedmétodos), isso não significa que o código de chamada esteja livre de condições de corrida em relação ao estado do singleton.
André Caron
8
Além disso, a estática não é contrária ao paradigma OOP. Muitos fanáticos por OOP dirão que a classe é um objeto, e o método estático é um método do objeto da classe, e não suas instâncias. Esse fenômeno está menos presente em Java. Outras linguagens, como Python, permitem usar classes como variáveis ​​e você pode acessar métodos estáticos como métodos desse objeto.
André Caron
4
A última linha do terceiro parágrafo deve ler todas as suas variáveis não estáticas , se não me engano.
Steve Steve
1
Object Lifetime, é um ponto muito importante que a @jessica mencionou.
Abhidemon
93

Mal é um termo subjetivo.

Você não controla a estática em termos de criação e destruição. Eles vivem a pedido do programa, carregando e descarregando.

Como as estáticas residem em um espaço, todos os threads que desejam usá-las devem passar pelo controle de acesso que você precisa gerenciar. Isso significa que os programas são mais acoplados e essa mudança é mais difícil de visualizar e gerenciar (como diz J Skeet). Isso leva a problemas de isolamento do impacto das mudanças e, portanto, afeta a maneira como os testes são gerenciados.

Estes são os dois principais problemas que tenho com eles.

Preet Sangha
fonte
59

Não. Os estados globais não são maus por si só. Mas precisamos ver seu código para ver se você o usou corretamente. É bem possível que um novato abuse dos estados globais; assim como ele abusaria de todos os recursos do idioma.

Estados globais são necessidade absoluta. Não podemos evitar estados globais. Não podemos evitar o raciocínio sobre estados globais. - Se queremos entender nossa semântica de aplicativos.

As pessoas que tentam se livrar dos estados globais por causa disso inevitavelmente acabam com um sistema muito mais complexo - e os estados globais ainda estão lá, disfarçados de maneira inteligente / idiota sob muitas camadas de indiretos; e ainda temos que raciocinar sobre estados globais, depois de desembrulhar todos os indiretos.

Como as pessoas da Primavera que declaram generosamente os estados globais em xml e pensam que de alguma forma é superior.

@ Jon Skeet if I create a new instance of an objectagora você tem duas razões para pensar: o estado dentro do objeto e o estado do ambiente que hospeda o objeto.

irreputável
fonte
10
"Eu tenho duas coisas para raciocinar". Não se eu fizer meu teste apenas dependente do estado do objeto. O que é mais fácil, menos estado global que tenho.
DJClayworth
2
A injeção de dependência não tem nada a ver com estado global ou visibilidade global - mesmo o contêiner em si não é global. Comparado ao código "normal", a única coisa extra que um objeto gerenciado por contêiner é visível é para o próprio contêiner. De fato, o DI é muito comumente usado para evitar o Padrão Singleton.
Floegipoky
31

Existem 2 problemas principais com variáveis ​​estáticas:

  • Segurança de threads - recursos estáticos, por definição, não são seguros para threads
  • Implicidade de código - você não sabe quando uma variável estática é instanciada e se ela será instanciada antes de outra variável estática
sternr
fonte
Acho que Jon Skeet estava se referindo ao mesmo comentário que você postou.
RG-3
13
Eu não entendo o ponto de segurança de threads, acho que nada é seguro para threads, a menos que você o faça. Isso não parece estar relacionado a coisas estáticas, corrija-me se estiver faltando alguma coisa.
Zmaster
1
@Zmaster - Embora seja verdade que thread-segurança não é um exclusivo questão a variáveis estáticas, porque, por sua definição, eles devem ser chamados a partir e por diferentes contextos, são mais de ameixa para eles
sternr
2
@sternr Entendo o que você quer dizer, evento se "contextos diferentes" não for necessariamente igual a "tópicos diferentes". Mas é verdade que a segurança de threads geralmente precisa ser levada em consideração com recursos estáticos. Você deve esclarecer a frase.
Zmaster
Existem usos seguros de encadeamento válidos de recursos estáticos, por exemplo. final estático privado Logger LOG = Logger.getLogger (Foo.class); final estático privado AtomicInteger x = novo AtomicInteger (0); Pelo que entendi, atribuições estáticas de recursos como este são garantidas com segurança de thread pelo carregador de classes. A instância do criador de logs é ou não é segura para threads, independentemente de onde você atribui o ponteiro a ela. Manter o estado em estática provavelmente não é uma boa ideia, mas não há razão para que ele não seja seguro para threads.
9788686_03_04_03-de-
29

Se você estiver usando a palavra-chave 'estática' sem a palavra-chave 'final', isso deve ser um sinal para considerar cuidadosamente seu design. Mesmo a presença de um 'final' não é um passe livre, pois um objeto final estático mutável pode ser igualmente perigoso.

Eu estimaria algo em torno de 85% das vezes que vejo uma 'estática' sem uma 'final', é ERRADO. Muitas vezes, vou encontrar soluções alternativas estranhas para mascarar ou ocultar esses problemas.

Por favor, não crie mutáveis ​​estáticos. Especialmente coleções. Em geral, as coleções devem ser inicializadas quando o objeto que os contém é inicializado e devem ser projetadas para que sejam redefinidas ou esquecidas quando o objeto que as contém é esquecido.

O uso de estática pode criar bugs muito sutis, o que causará muitos dias de sofrimento aos engenheiros. Eu sei, porque eu criei e caço esses bugs.

Se você quiser obter mais detalhes, leia…

Por que não usar estática?

Há muitos problemas com a estática, incluindo a escrita e a execução de testes, além de erros sutis que não são imediatamente óbvios.

O código que depende de objetos estáticos não pode ser facilmente testado em unidade, e as estáticas não podem ser facilmente ridicularizadas (geralmente).

Se você usa estática, não é possível trocar a implementação da classe para testar componentes de nível superior. Por exemplo, imagine um CustomerDAO estático que retorne objetos Customer que ele carrega do banco de dados. Agora eu tenho uma classe CustomerFilter, que precisa acessar alguns objetos Customer. Se o CustomerDAO for estático, não posso escrever um teste para o CustomerFilter sem primeiro inicializar meu banco de dados e preencher informações úteis.

E a população e a inicialização do banco de dados levam muito tempo. E, na minha experiência, sua estrutura de inicialização do banco de dados mudará com o tempo, o que significa que os dados se transformarão e os testes poderão ser interrompidos. IE, imagine que o Cliente 1 costumava ser um VIP, mas a estrutura de inicialização do DB mudou e agora o Cliente 1 não é mais VIP, mas seu teste foi codificado para carregar o Cliente 1…

Uma abordagem melhor é instanciar um CustomerDAO e passá-lo para o CustomerFilter quando ele é construído. (Uma abordagem ainda melhor seria usar o Spring ou outra estrutura de Inversão de controle.

Depois de fazer isso, você pode rapidamente zombar ou deletar um DAO alternativo no CustomerFilterTest, permitindo que você tenha mais controle sobre o teste,

Sem o DAO estático, o teste será mais rápido (sem inicialização do banco de dados) e mais confiável (porque não falhará quando o código de inicialização do banco de dados for alterado). Por exemplo, nesse caso, garantir que o Cliente 1 seja e sempre será um VIP, no que diz respeito ao teste.

Executando testes

A estática causa um problema real ao executar conjuntos de testes de unidade juntos (por exemplo, com o servidor de Integração Contínua). Imagine um mapa estático de objetos Socket de rede que permaneçam abertos de um teste para outro. O primeiro teste pode abrir um soquete na porta 8080, mas você esqueceu de limpar o mapa quando o teste for interrompido. Agora, quando um segundo teste é iniciado, é provável que ocorra um erro fatal ao tentar criar um novo soquete para a porta 8080, pois a porta ainda está ocupada. Imagine também que as referências de soquete em sua coleção estática não sejam removidas e (com exceção do WeakHashMap) nunca são elegíveis para serem coletadas como lixo, causando um vazamento de memória.

Este é um exemplo super generalizado, mas em sistemas grandes, esse problema ocorre o tempo todo. As pessoas não pensam nos testes de unidade iniciando e parando seus softwares repetidamente na mesma JVM, mas é um bom teste para o design de seu software e, se você deseja obter alta disponibilidade, é algo que precisa estar ciente.

Esses problemas geralmente surgem com objetos de estrutura, por exemplo, suas camadas de acesso ao banco de dados, cache, sistema de mensagens e log. Se você estiver usando Java EE ou algumas das melhores estruturas de criação, provavelmente elas gerenciam muito disso para você, mas se, como eu, você está lidando com um sistema legado, pode ter muitas estruturas personalizadas para acessar essas camadas.

Se a configuração do sistema que se aplica a esses componentes da estrutura for alterada entre os testes de unidade, e a estrutura de teste de unidade não desmontar e reconstruir os componentes, essas alterações não terão efeito e, quando um teste depender dessas alterações, elas falharão. .

Mesmo componentes que não sejam de estrutura estão sujeitos a esse problema. Imagine um mapa estático chamado OpenOrders. Você escreve um teste que cria alguns pedidos em aberto e verifica se todos estão no estado correto e o teste termina. Outro desenvolvedor escreve um segundo teste que coloca os pedidos de que precisa no mapa OpenOrders e afirma que o número de pedidos é preciso. Executar individualmente, esses testes passariam, mas quando executados juntos em um conjunto, eles falhariam.

Pior, a falha pode ser baseada na ordem em que os testes foram executados.

Nesse caso, evitando a estática, você evita o risco de persistência de dados nas instâncias de teste, garantindo melhor confiabilidade do teste.

Erros sutis

Se você trabalha no ambiente de alta disponibilidade ou em qualquer lugar em que os encadeamentos possam ser iniciados e parados, a mesma preocupação mencionada acima com os conjuntos de testes de unidade pode ser aplicada quando o código também estiver em produção.

Ao lidar com threads, em vez de usar um objeto estático para armazenar dados, é melhor usar um objeto inicializado durante a fase de inicialização do thread. Dessa maneira, toda vez que o encadeamento é iniciado, uma nova instância do objeto (com uma configuração potencialmente nova) é criada e você evita que os dados de uma instância do encadeamento fluam para a próxima instância.

Quando um thread morre, um objeto estático não é redefinido ou coletado como lixo. Imagine que você tenha um segmento chamado "EmailCustomers" e, quando iniciado, preenche uma coleção estática de String com uma lista de endereços de email e começa a enviar cada um dos endereços. Digamos que o encadeamento seja interrompido ou cancelado de alguma forma, portanto sua estrutura de alta disponibilidade reinicia o encadeamento. Então, quando o encadeamento é iniciado, ele recarrega a lista de clientes. Mas como a coleção é estática, ela pode reter a lista de endereços de email da coleção anterior. Agora, alguns clientes podem receber e-mails duplicados.

An Aside: Final Estático

O uso de "final estático" é efetivamente o equivalente em Java de um C # definido, embora haja diferenças técnicas de implementação. AC / C ++ #define é trocado de código pelo pré-processador, antes da compilação. Um Java "estático final" acabará com a memória residente na pilha. Dessa forma, é mais semelhante a uma variável "const estática" em C ++ do que a um #define.

Sumário

Espero que isso ajude a explicar algumas razões básicas pelas quais a estática é problemática. Se você estiver usando uma estrutura Java moderna, como Java EE ou Spring, etc, poderá não encontrar muitas dessas situações, mas se estiver trabalhando com um grande corpo de código legado, elas poderão se tornar muito mais frequentes.

JBCP
fonte
15

Como ninguém * mencionou: simultaneidade. As variáveis ​​estáticas podem surpreendê-lo se você tiver vários threads lendo e gravando na variável estática. Isso é comum em aplicativos da web (por exemplo, ASP.NET) e pode causar alguns erros bastante enlouquecedores. Por exemplo, se você tiver uma variável estática atualizada por uma página e a página for solicitada por duas pessoas "quase ao mesmo tempo", um usuário poderá obter o resultado esperado pelo outro usuário ou pior.

estática reduz as interdependências nas outras partes do código. Eles podem atuar como detentores de estado perfeitos

Espero que você esteja preparado para usar bloqueios e lidar com disputas.

* Na verdade, Preet Sangha mencionou.

Justin M. Keyes
fonte
5
Variáveis ​​de instância não têm vantagens de segurança de encadeamento sobre estática, todas elas são variáveis ​​desprotegidas. Em vez disso, tudo se resume a como você protege o código que acessa essas variáveis.
Studgeek 17/08/11
2
Não fiz essa afirmação, mas por uma questão de discussão: a separação é uma forma de proteção. Os estados do thread são separados; estado global não é . Uma variável de instância não precisa de proteção, a menos que seja explicitamente compartilhada entre threads; uma variável estática é sempre compartilhada por todos os threads no processo.
Justin M. Keyes
Eu gostaria que as variáveis ​​estáticas do thread fossem mais um conceito de primeira classe, pois podem ser muito úteis para disponibilizar informações com segurança para uma chamada de sub-rotina empacotada sem precisar passar essas informações por todas as camadas de empacotamento. Por exemplo, se um objeto tivesse métodos para renderizá-lo no contexto gráfico atual do encadeamento e houvesse métodos para salvar / restaurar o contexto gráfico atual, usá-los geralmente seria mais limpo do que ter que passar o contexto gráfico por todas as chamadas de método.
supercat
15

Resumindo algumas vantagens e desvantagens básicas do uso de métodos estáticos em Java:

Vantagens:

  1. Acessível globalmente, ou seja, não está vinculado a nenhuma instância de objeto específica.
  2. Uma instância por JVM.
  3. Pode ser acessado usando o nome da classe (nenhum objeto requer).
  4. Contém um valor único aplicável a todas as instâncias.
  5. Carregue na inicialização da JVM e morre quando a JVM é encerrada.
  6. Eles não modificam o estado do objeto.

Desvantagens:

  1. Membros estáticos sempre fazem parte da memória, estejam eles em uso ou não.
  2. Você não pode controlar a criação e destruição de variável estática. Útil, eles foram criados no carregamento do programa e destruídos quando o programa é descarregado (ou quando a JVM é encerrada).
  3. Você pode tornar o thread estático seguro usando a sincronização, mas precisa de alguns esforços extras.
  4. Se um encadeamento alterar o valor de uma variável estática que pode possivelmente interromper a funcionalidade de outros encadeamentos.
  5. Você deve saber "estático" antes de usá-lo.
  6. Você não pode substituir métodos estáticos.
  7. A serialização não funciona bem com eles.
  8. Eles não participam do polimorfismo de tempo de execução.
  9. Há um problema de memória (até certo ponto, mas não muito, eu acho) se um grande número de variáveis ​​/ métodos estáticos for usado. Porque eles não serão coletados como lixo até o programa terminar.
  10. Métodos estáticos também são difíceis de testar.
Affy
fonte
As desvantagens 6, 7, 8 e 10 são as desvantagens das linguagens / estruturas usadas, e não as desvantagens das variáveis ​​estáticas em geral. As desvantagens 1, 4 e 5 existem também para outras soluções, como algum padrão singleton fornecido por alguma estrutura. (Eu não votei para a resposta, porque eu concordo o resto e é uma coleção agradável.)
peterh - Reintegrar Monica
13

se eu tivesse que fazer 10.000 chamadas para uma função dentro de uma classe, ficaria feliz em tornar o método estático e usar uma classe direta.methodCall () nele, em vez de sobrecarregar a memória com 10.000 instâncias da classe, certo?

Você precisa equilibrar a necessidade de encapsular dados em um objeto com um estado, versus a necessidade de simplesmente computar o resultado de uma função em alguns dados.

Além disso, a estática reduz as interdependências nas outras partes do código.

O mesmo acontece com o encapsulamento. Em aplicações grandes, as estáticas tendem a produzir código espaguete e não permitem facilmente refatoração ou teste.

As outras respostas também fornecem boas razões contra o uso excessivo de estática.

Jérôme Verstrynge
fonte
13

As variáveis ​​estáticas são geralmente consideradas ruins porque representam o estado global e, portanto, são muito mais difíceis de raciocinar. Em particular, eles quebram as suposições da programação orientada a objetos. Na programação orientada a objetos, cada objeto tem seu próprio estado, representado por variáveis ​​de instância (não estáticas). As variáveis ​​estáticas representam o estado entre instâncias, o que pode ser muito mais difícil de testar na unidade. Isso ocorre principalmente porque é mais difícil isolar alterações em variáveis ​​estáticas em um único teste.

Dito isto, é importante fazer uma distinção entre variáveis ​​estáticas regulares (geralmente consideradas ruins) e variáveis ​​estáticas finais (constantes AKA; não tão ruins).

Jack Edmonds
fonte
4
"Variáveis ​​estáticas representam estado entre classes" ... Eu acho que você quer dizer "variáveis ​​estáticas representam estado entre instâncias"? +1 para "constantes AKA estáticas finais, não é tão ruim". Como o valor não pode mudar, tudo o que depende dele em um momento não pode implicitamente mudar seu comportamento posteriormente - o valor é o mesmo.
Jared Updike
"Variáveis ​​estáticas representam estado entre instâncias" é uma maneira muito melhor de declarar isso. Eu editei minha resposta.
21711 Jack Edmonds
9

Na minha opinião, quase nunca é sobre desempenho, é sobre design. Eu não considero o uso de métodos estáticos errado, ao contrário do uso de variáveis ​​estáticas (mas acho que você está realmente falando sobre chamadas de método).

É simplesmente sobre como isolar a lógica e dar a ela um bom lugar. Às vezes, isso justifica o uso de métodos estáticos, dos quais java.lang.Mathé um bom exemplo. Eu acho que quando você nomeia a maioria de suas aulas XxxUtilou Xxxhelperé melhor reconsiderar seu design.

M Platvoet
fonte
3
Métodos estáticos livres de efeitos colaterais puros são perfeitamente adequados para IMO. Mas o estado global mutável raramente é e eu interpreto o OP como falando sobre o estado global.
CodesInChaos
1
@CodeInChaos concorda totalmente. Acho que o OP não é totalmente claro sobre a diferença entre métodos estáticos e vars.
M Platvoet
8

Acabei de resumir alguns dos pontos apresentados nas respostas. Se você encontrar algo errado, sinta-se à vontade para corrigi-lo.

Dimensionamento: Temos exatamente uma instância de uma variável estática por JVM. Suponha que estamos desenvolvendo um sistema de gerenciamento de bibliotecas e decidimos colocar o nome do livro em uma variável estática, pois existe apenas um por livro. Mas se o sistema cresce e estamos usando várias JVMs, não temos como descobrir com qual livro estamos lidando?

Segurança de thread: Tanto a variável de instância quanto a variável estática precisam ser controladas quando usadas em ambientes multithread. Porém, no caso de uma variável de instância, ela não precisa de proteção, a menos que seja explicitamente compartilhada entre os encadeamentos, mas no caso de uma variável estática, ela sempre é compartilhada por todos os encadeamentos no processo.

Teste: embora o design testável não seja igual ao bom design, mas raramente observaremos um bom design que não é testável. Como as variáveis ​​estáticas representam o estado global e fica muito difícil testá-las.

Raciocínio sobre o estado: Se eu criar uma nova instância de uma classe, podemos raciocinar sobre o estado dessa instância, mas se ela tiver variáveis ​​estáticas, poderá estar em qualquer estado. Por quê? Porque é possível que a variável estática tenha sido modificada por alguma instância diferente, pois a variável estática é compartilhada entre instâncias.

Serialização: A serialização também não funciona bem com eles.

Criação e destruição: a criação e a destruição de variáveis ​​estáticas não podem ser controladas. Geralmente eles são criados e destruídos no tempo de carregamento e descarregamento do programa. Isso significa que eles são ruins para o gerenciamento de memória e também aumentam o tempo de inicialização na inicialização.

Mas e se realmente precisarmos deles?

Mas, às vezes, podemos ter uma necessidade genuína deles. Se realmente sentimos a necessidade de muitas variáveis ​​estáticas compartilhadas no aplicativo, uma opção é fazer uso do padrão Singleton Design, que terá todas essas variáveis. Ou podemos criar algum objeto que tenha essas variáveis ​​estáticas e possa ser transmitido.

Além disso, se a variável estática estiver marcada como final, ela se tornará uma constante e o valor atribuído a ela uma vez não poderá ser alterado. Isso significa que nos salvará de todos os problemas que enfrentamos devido à sua mutabilidade.

akhil_mittal
fonte
7

Parece-me que você está perguntando sobre variáveis ​​estáticas, mas também aponta métodos estáticos em seus exemplos.

Variáveis ​​estáticas não são más - elas são adotadas como variáveis ​​globais, como constantes, na maioria dos casos combinadas com o modificador final, mas como foi dito, não as use demais.

Métodos estáticos, também conhecido como método utilitário. Geralmente, não é uma prática ruim usá-los, mas a grande preocupação é que eles possam obstruir os testes.

Como exemplo de um ótimo projeto java que usa muita estática e o faz da maneira correta, veja o Play! estrutura . Também há discussões sobre isso no SO.

Variáveis ​​/ métodos estáticos combinados à importação estática também são amplamente utilizados em bibliotecas que facilitam a programação declarativa em java, como: facilite ou Hamcrest . Não seria possível sem muitas variáveis ​​e métodos estáticos.

Portanto, variáveis ​​estáticas (e métodos) são boas, mas use-as com sabedoria!

cetnar
fonte
6

As variáveis ​​estáticas criam mais importante o problema com a segurança dos dados (a qualquer momento, qualquer pessoa pode mudar, acesso direto sem objeto, etc.)

Para mais informações, leia isto Obrigado.

Anuj Patel
fonte
6

Pode-se sugerir que na maioria dos casos em que você usa uma variável estática, você realmente deseja usar o padrão singleton .

O problema com os estados globais é que, às vezes, o que faz sentido como global em um contexto mais simples, precisa ser um pouco mais flexível em um contexto prático, e é aí que o padrão singleton se torna útil.

Charles Goodwin
fonte
5

Mais um motivo: fragilidade.

Se você tem uma aula, a maioria das pessoas espera poder criá-la e usá-la à vontade.

Você pode documentar que não é o caso ou se proteger (padrão de singleton / fábrica) - mas isso é trabalho extra e, portanto, um custo adicional. Mesmo assim, em uma grande empresa, é provável que alguém tente em algum momento usar sua classe sem prestar atenção total a todos os bons comentários ou à fábrica.

Se você estiver usando muitas variáveis ​​estáticas, isso será interrompido. Bugs são caros.

Entre uma melhoria de desempenho de 0,0001% e robustez a ser alterada por desenvolvedores potencialmente sem noção, em muitos casos a robustez é a boa escolha.

ptyx
fonte
4

Acho variáveis ​​estáticas mais convenientes de usar. E presumo que eles também sejam eficientes (por favor, corrija-me se estiver errado), porque se eu tivesse que fazer 10.000 chamadas para uma função dentro de uma classe, ficaria feliz em tornar o método estático e usar uma classe direta.methodCall () nele em vez de bagunçar a memória com 10.000 instâncias da classe, certo?

Entendo o que você pensa, mas um simples padrão Singleton fará o mesmo sem precisar instanciar 10.000 objetos.

métodos estáticos podem ser usados, mas apenas para funções relacionadas ao domínio do objeto e não precisam nem usam propriedades internas do objeto.

ex:

public class WaterContainer {
    private int size;
    private int brand;
    ...etc

    public static int convertToGallon(int liters)...

    public static int convertToLiters(int gallon)...

}
Cygnusx1
fonte
Um singleton clássico (ou seja, um acessado por Class.Instance) é pouco melhor do que uma variável estática. É um pouco mais testável, mas ainda muito pior do que os designs em que você cria uma única instância em vez de criar seu código na suposição de que há apenas uma.
CodesInChaos
Não sei se entendi o seu comentário! eu estava respondendo ao OP sobre o que ele declarou em itálico sobre instanciar 10.000 objetos. Eu não entendo por que você compara um singleton e uma variável estática? O que eu entendo pelo que você escreveu é que Singleton é um design ruim ...! Eu acho que entendê-lo mal, uma vez que a marca Spring Framework por padrão todos os feijões Singleton ;-)
Cygnusx1
Um singleton clássico (que possui Class.Instance) que carrega um estado mutável é um design IMO ruim. Nesse caso, eu prefiro fortemente um design em que obtenho os singletons necessários para passar como parâmetro para a classe que os utiliza (normalmente com a ajuda do DI). Singletons clássicos logicamente imutáveis ​​são excelentes para IMO.
CodesInChaos
@ Cygnusx1 Caso não estivesse claro por que um singleton de Classe (um singleton em que a classe garante uma cópia única) não era facilmente testável, associa fortemente a existência de classe ao ciclo de vida do programa. Para testá-lo, você deve aderir ao lançamento e desligamento do programa, que geralmente tem efeitos colaterais sem importância para testar a classe. Se fosse efetivamente como singleton (uma cópia no programa, mas não a execução de outra forma), você poderia criar várias cópias no tempo de teste sem o programa, verificando se o comportamento na classe é o que deveria ser para cada cenário de teste.
Edwin Buck #
4

A questão de 'Statics being evil' é mais uma questão sobre o estado global. O momento apropriado para uma variável ser estática é se ela nunca tiver mais de um estado; As ferramentas do IE que devem estar acessíveis por toda a estrutura e sempre retornar os mesmos resultados para as mesmas chamadas de método nunca são 'más' como as estáticas. Quanto ao seu comentário:

Acho variáveis ​​estáticas mais convenientes de usar. E presumo que eles também sejam eficientes

A estática é a escolha ideal e eficiente para variáveis ​​/ classes que nunca mudam .

O problema com o estado global é a inconsistência inerente que ele pode criar. A documentação sobre testes de unidade geralmente aborda esse problema, pois sempre que existe um estado global que pode ser acessado por mais de vários objetos não relacionados, seus testes de unidade ficam incompletos e não são granulados por 'unidade'. Conforme mencionado neste artigo sobre estado global e singletons , se os objetos A e B não estiverem relacionados (como em um não é expressamente dada referência a outro), então A não poderá afetar o estado de B.

Existem algumas exceções ao estado global de proibição em bom código, como o relógio. O tempo é global e, em certo sentido, altera o estado dos objetos sem ter um relacionamento codificado.

Bryan Agee
fonte
"O tempo é global" - há outras maneiras de modelar o tempo nos sistemas de computação, além de fazer com que algo implícito e global mude por conta própria. cf. esta pesquisa: "Modelando o tempo na computação: uma taxonomia e uma pesquisa comparativa" @ arxiv.org/abs/0807.4132
Jared Updike
4

Meu $ .02 é que várias dessas respostas estão confundindo a questão, em vez de dizer "estática é ruim". Acho melhor falar sobre escopo e instâncias.

O que eu diria é que uma estática é uma variável de "classe" - representa um valor que é compartilhado em todas as instâncias dessa classe. Normalmente, o escopo também deve ser definido dessa maneira (protegido ou privado para a classe e suas instâncias).

Se você planeja colocar o comportamento em nível de classe em torno dele e expô-lo a outro código, um singleton pode ser uma solução melhor para suportar mudanças no futuro (como @ Jessica sugeriu). Isso ocorre porque você pode usar interfaces no nível da instância / singleton de maneiras que não podem ser usadas no nível da classe - em particular na herança.

Algumas reflexões sobre por que acho que alguns dos aspectos em outras respostas não são essenciais para a pergunta ...

A estática não é "global". No Java, o escopo é controlado separadamente do estático / instância.

A simultaneidade não é menos perigosa para a estática do que os métodos de instância. Ainda é um estado que precisa ser protegido. Claro que você pode ter 1000 instâncias com uma variável de instância, cada uma delas apenas uma variável estática, mas se o código acessado não for gravado de uma maneira segura para threads, você ainda está ferrado - pode demorar um pouco mais para você perceber. .

Gerenciar o ciclo de vida é um argumento interessante, mas acho que é menos importante. Não vejo por que é mais difícil gerenciar um par de métodos de classe como init () / clear () do que a criação e destruição de uma instância singleton. De fato, alguns podem dizer que um singleton é um pouco mais complicado devido ao GC.

PS: Em termos de Smalltalk, muitos de seus dialetos têm variáveis ​​de classe, mas, em Smalltalk, as classes são na verdade instâncias do Metaclass e, na verdade, são variáveis ​​na instância do Metaclass. Mesmo assim, eu aplicaria a mesma regra de ouro. Se eles estão sendo usados ​​para estado compartilhado entre instâncias, ok. Se eles oferecem suporte à funcionalidade pública, você deve procurar um Singleton. Suspiro, com certeza sinto falta de Smalltalk ....

studgeek
fonte
4

Há duas perguntas principais em sua postagem.

Primeiro, sobre variáveis ​​estáticas. Variáveis ​​estáticas são totalmente desnecessárias e seu uso pode ser evitado facilmente. Em idiomas OOP em geral, e em Java em particular, os parâmetros de função são colados por referência, ou seja, se você passa um objeto para um funciont, está passando um ponteiro para o objeto, portanto, não é necessário definir variáveis ​​estáticas, pois você pode passar um ponteiro para o objeto para qualquer escopo que precise dessas informações. Mesmo que isso implique que você preencherá sua memória com ponteiros, isso não será necessário para um desempenho ruim, porque os sistemas de paginação de memória reais são otimizados para lidar com isso e manterão na memória as páginas referenciadas pelos ponteiros que você passou para o novo escopo; o uso de variáveis ​​estáticas pode fazer com que o sistema carregue a página de memória onde estão armazenadas quando precisam ser acessadas (isso acontecerá se a página não for acessada há muito tempo). Uma boa prática é reunir todo esse material estático em alguns pequenos "clases de configuração", isso garantirá que o sistema coloque tudo na mesma página de memória.

Segundo, sobre métodos estáticos. Os métodos estáticos não são tão ruins, mas podem reduzir rapidamente o desempenho. Por exemplo, pense em um método que compara dois objetos de uma classe e retorne um valor indicando qual dos objetos é maior (método típico de comparação). Esse método pode ser estático ou não, mas ao invocá-lo, a forma não estática será mais eficiente pois ele terá que resolver apenas duas referências (uma para cada objeto) face às três referências que terão que resolver a versão estática do mesmo método (uma para a classe mais duas, uma para cada objeto). Mas, como eu disse, isso não é tão ruim, se dermos uma olhada na classe Math, podemos encontrar muitas funções matemáticas definidas como métodos estáticos. Isso é realmente mais eficiente do que colocar todos esses métodos na classe que define os números,

Conclusão: evite o uso de variáveis ​​estáticas e encontre o equilíbrio correto de desempenho ao lidar com métodos estáticos ou não estáticos.

PS: Desculpe pelo meu inglês.

jrodriguez
fonte
4

Não há nada errado com variáveis ​​estáticas em si. É apenas a sintaxe Java que está quebrada. Cada classe Java realmente define duas estruturas - um objeto singleton que encapsula variáveis ​​estáticas e uma instância. Definir os dois no mesmo bloco de origem é pura maldade e resulta em um código difícil de ler. Scala fez isso certo.

Zdeněk Pavlas
fonte
3

a) Razão sobre os programas.

Se você tem um programa de pequeno a médio porte, onde a variável estática Global.foo é acessada, a chamada para ela normalmente não vem do nada - não há caminho e, portanto, não há cronograma, como a variável chega ao local, onde é usado. Agora, como sei quem definiu o valor real? Como sei o que acontece se eu o modificar agora? Eu tenho grep sobre toda a fonte, para coletar todos os acessos, para saber o que está acontecendo.

Se você souber como usá-lo, porque acabou de escrever o código, o problema é invisível, mas se você tentar entender código estrangeiro, entenderá.

b) Você realmente precisa apenas de um?

Variáveis ​​estáticas geralmente impedem que vários programas do mesmo tipo sejam executados na mesma JVM com valores diferentes. Geralmente, você não prevê usos, onde mais de uma instância do seu programa é útil, mas se ela evoluir, ou se for útil para outras pessoas, elas poderão enfrentar situações em que gostariam de iniciar mais de uma instância do seu programa. .

Somente código mais ou menos inútil, que não será usado por muitas pessoas por um longo período de forma intensiva, pode funcionar bem com variáveis ​​estáticas.

Usuário desconhecido
fonte
3

tudo (pode :) ter seu objetivo, se você tiver um monte de threads que precisam compartilhar / armazenar dados em cache e também toda a memória acessível (para não se dividir em contextos em uma JVM), a estática é a melhor escolha

-> é claro que você pode forçar apenas uma instância, mas por quê?
Eu acho que alguns dos comentários neste tópico são maus, não estáticos;)

tomasb
fonte
3

Variáveis ​​estáticas não são boas nem más. Eles representam atributos que descrevem toda a classe e não uma instância específica. Se você precisar de um contador para todas as instâncias de uma determinada classe, uma variável estática seria o local certo para armazenar o valor.

Os problemas aparecem quando você tenta usar variáveis ​​estáticas para manter valores relacionados à instância.

Andres
fonte
2

Todas as respostas acima mostram por que as estáticas são ruins. A razão pela qual eles são maus é porque isso dá a falsa impressão de que você está escrevendo código orientado a objetos, quando na verdade não está. Isso é simplesmente mau.

estúpido
fonte
1
Mas, considerando rigidamente o seu código para seguir algum paradigma arbitrário padrão, ele realmente melhora ou estamos reclamando para evitar apenas escrever código que funcione?
21411 Zoey
Sim, melhora, porque torna mais gerenciável no futuro, mais fácil de entender e mais explícita.
blockhead
1
Por que é ruim não escrever código OO? e por que Bjarne Stroustrup não concorda com você? para citar apenas um ...
Marquês de Lorne
2
Eu não disse que é ruim não escrever código OO. Eu disse que é ruim pensar que você está escrevendo código OO, quando tudo o que você é é disfarçar os globais por trás de métodos e propriedades estáticas. Por favor, releia o que escrevi.
blockhead
2

Há muitas boas respostas aqui, adicionando a ela,

Memória: as variáveis ​​estáticas permanecem ativas enquanto o carregador de classes permanecer [em geral até a VM morrer], mas isso ocorre apenas no caso de objetos em massa / referências armazenados como estáticos.

Modularização: considere conceitos como IOC, dependencyInjection, proxy etc. Todos são totalmente contra implementações estáticas / de acoplamento rígido.

Outros Contras: Segurança de Rosca, Testabilidade

Sankarganesh Eswaran
fonte
0

Pense que, se você tiver um aplicativo com muitos usuários e definir um formulário estático, todos os usuários também modificarão todos os outros formulários.

seinta
fonte
0

Eu acho que o uso excessivo de variáveis ​​globais com palavra-chave estática também levará ao vazamento de memória em algum momento da aplicação

Rahul Anand
fonte
0

Do meu ponto de vista, a staticvariável deve ser apenas leitura de dados ou variáveis ​​criadas por convenção .

Por exemplo, temos uma interface do usuário de algum projeto e temos uma lista de países, idiomas, funções de usuário etc. E temos aulas para organizar esses dados. Temos certeza absoluta de que o aplicativo não funcionará sem essas listas. portanto, o primeiro que fazemos no init do aplicativo é verificar esta lista para atualizações e obter essa lista da API (se necessário). Portanto, concordamos que esses dados estão "sempre" presentes no aplicativo. São dados apenas para leitura, portanto, não precisamos cuidar do seu estado - pensando nesse caso, não queremos ter muitas instâncias desses dados - esse caso parece um candidato perfeito para ser estático .

qiAlex
fonte
0

Eu brinquei muito com estática e posso lhe dar uma resposta um pouco diferente - ou talvez uma maneira um pouco diferente de encará-la?

Quando eu usei estática em uma classe (membros e métodos, ambos), comecei a perceber que minha classe é na verdade duas classes que compartilham responsabilidades - existe a parte "Estática", que age muito como um singleton e existe a não parte estática (uma classe normal). Tanto quanto eu sei, você sempre pode separar completamente essas duas classes apenas selecionando todas as estáticas para uma classe e as não estáticas para a outra.

Isso costumava acontecer muito quando eu tinha uma coleção estática dentro de uma classe contendo instâncias da classe e alguns métodos estáticos para gerenciar a coleção. Quando você pensa sobre isso, é óbvio que sua turma não está fazendo "Apenas uma coisa", é uma coleção e está fazendo algo completamente diferente.

Agora, vamos refatorar um pouco o problema: se você dividir sua classe em uma classe onde tudo é estático e outro que é apenas uma "Classe Normal" e esquecer a "Classe Normal", sua pergunta se tornará puramente estática versus Singleton, que é abordada em comprimento aqui (e provavelmente uma dúzia de outras questões).

Bill K
fonte