Não use "Static" em C #?


109

Enviei uma inscrição que escrevi para alguns outros arquitetos para revisão de código. Um deles quase imediatamente me escreveu de volta e disse: "Não use" estático ". Você não pode escrever testes automatizados com classes e métodos estáticos." Estático "deve ser evitado."

Eu verifiquei e totalmente 1/4 das minhas aulas estão marcadas como "estáticas". Uso estático quando não vou criar uma instância de uma classe porque a classe é uma única classe global usada em todo o código.

Ele passou a mencionar algo que envolvia zombaria, técnicas IOC / DI que não podem ser usadas com código estático. Ele diz que é lamentável quando as bibliotecas de terceiros são estáticas devido à sua falta de testabilidade.

Esse outro arquiteto está correto?

update: aqui está um exemplo:

APIManager - essa classe mantém dicionários de APIs de terceiros que estou chamando junto com o próximo tempo permitido. Ele impõe limites de uso da API que muitos terceiros têm em seus termos de serviço. Eu o uso em qualquer lugar em que estou chamando um serviço de terceiros, chamando Thread.Sleep (APIManager.GetWait ("ProviderXYZ")); antes de fazer a ligação. Tudo aqui é seguro para threads e funciona muito bem com o TPL em C #.


37
staticestá bem; static campos precisam ser tratados com muito cuidado
Marc Gravell

2
Por que ele escreveria testes para bibliotecas de terceiros? Você não costuma testar seu próprio código, supondo que os criadores da biblioteca de terceiros tenham feito o teste?
Nope

3
Essa objeção em particular é geral demais para mim. Na maioria dos casos, você não modificaria a classe estática, zombaria dos argumentos. Campo estático em que era uma classe agregada que precisava de zombaria, então sim.

8
Claramente, seus colegas têm uma condição séria - um OOPus eritematoso sistêmico agudo. Eles precisam de um tratamento o mais rápido possível!
SK-logic

6
Há uma seção de respostas para fornecer respostas, não entendo por que as pessoas comentam na tentativa de dar a alguém uma resposta / sugestão ... Isso derrota o objetivo do StackExchange, certamente um comentário seria solicitar mais informações.
peteski 11/09/12

Respostas:


121

Depende se as classes estáticas mantêm o estado ou não. Pessoalmente, não tenho nenhum problema com funções sem estado lançadas juntas em uma classe estática.


111
Corrigir. Funções estáticas que não apresentam efeitos colaterais são as funções mais testáveis ​​de todas.
Robert Harvey

9
+1 a isso, mas com uma cláusula de segurança: quase todo algoritmo abstrato é sem estado, mas isso não significa que você deve implementá-lo como classe ou método estático sem estado. Implementá-lo dessa maneira faz com que todo o código que o utiliza dificilmente esteja vinculado a ele. Isso significa, por exemplo, que se você implementar o algoritmo de classificação como um método estático e usá-lo através do projeto - não poderá substituir temporariamente a classificação por outro algoritmo, ou seja, para fins de teste e para restringir a área do problema. Este é um enorme obstáculo em muitos casos. Além disso, a estática é totalmente boa se usada razoavelmente.

11
Resumindo: são as funções mais testáveis, mas não é necessário fazer o outro código mais testável! Isso é o que o arquiteto quis dizer provavelmente.

4
@ tereško: A linguagem C # requer que os métodos estáticos façam parte de uma classe estática, se você não quiser criar uma instância da classe para chamar o método. Talvez você queira dizer "instância" e não "classe".
Robert Harvey

8
@quetzalcoatl: É perfeitamente legal passar um delegado (ou seja, um algoritmo diferente) para um método estático; os métodos de extensão no Linq são todos métodos estáticos. Exemplo: msdn.microsoft.com/en-us/library/…
Robert Harvey

93

Ele é muito geral sobre isso. Ele está correto, dificulta o teste. No entanto, staticclasses e métodos têm seu lugar e isso realmente depende do contexto. Sem exemplos de código, você não pode realmente dizer.

Uso estático quando não vou criar uma instância de uma classe porque a classe é uma única classe global usada em todo o código.

Esse pode ser um cheiro severo ao código. Para que você está usando as aulas? Para armazenar dados? Para acessar um banco de dados? Então ele está correto. Nesse caso, você deve examinar a injeção de dependência, pois essa classe estática é efetivamente um singleton implícito.

Se você os estiver usando para métodos de extensão ou auxiliares que não alteram o estado e apenas operam com os parâmetros que você fornece, geralmente eles estão bem.


3
+1 por mencionar que a declaração era geral e mencionar métodos de extensão, que também podem ser testados e as dependências ainda podem ser zombadas, tanto quanto eu sei.
Nope

4
Aye estática como instância de classe única como um solteirão implícita, que vai ficar confuso ....

"Para acessar um banco de dados?" Por que não ? se o adaptador de tabela não for estático, crie um conjunto de dados de classe estática com tipagem forte, nada de errado com ele ... a menos que você prove seu ponto de vista com referência ou exemplo?
Matemática

27

Eu verifiquei e totalmente 1/4 das minhas aulas estão marcadas como "estáticas". Uso estático quando não vou criar uma instância de uma classe porque a classe é uma única classe global usada em todo o código.

A melhor coisa a fazer é tentar testar seu código de unidade. Tente projetar testes que sejam repetíveis, independentes, simples e testem apenas um método por vez. Tente executar seus testes em ordem aleatória diferente. Você pode obter uma compilação "verde" estável?

Se sim, esse é um ponto válido para defender seu código. Se, no entanto, você tiver dificuldades, talvez deva usar métodos baseados em instância.


7
Este é o ponto que me impressionou também. Algumas classes de estilo 'auxiliar' estáticas e sem estado são boas, mas se você tem 25% de todas as suas classes como estáticas, é improvável que seus esforços sejam limitados a esse caso.
Matthew Scharley

2
@MatthewScharley - ele provavelmente tem 4 classes, e um deles é estática :)
Alexus

1
Observe que, se você usar esses stibcs (como singletons), criar mais instâncias posteriormente poderá ser um trabalho difícil. Por exemplo, ao criar um aplicativo que lida com um documento, você enfrenta problemas mais tarde quando deseja gerenciar vários documentos.
Michel Keijzers

11

As respostas já publicadas abrangem muitos pontos realmente bons, mas há um que parece estar faltando:

Os campos estáticos nunca são coletados como lixo.

Isso é realmente importante se você tiver um aplicativo com muitas restrições de memória e esse padrão pode ser muito comum quando as pessoas tentam implementar um cache.

As funções estáticas não são tão ruins, mas todo mundo já cobriu isso com detalhes suficientes.


10

Uma das vantagens que você obtém do IoC / DI é que a maioria das interações entre classes é negociada entre interfaces. Isso facilita o teste de unidade, porque as interfaces podem ser simuladas automaticamente ou semi-automaticamente e, portanto, cada uma das partes pode ser testada quanto a entradas e saídas. Além disso, além da testabilidade, a colocação de interfaces entre tudo permite que você tenha o código mais modular possível - você pode substituir facilmente uma implementação sem muita preocupação, pois está estragando as dependências.

Como o C # não possui metaclasses, uma classe não pode implementar uma interface usando recursos estáticos; portanto, os recursos estáticos das classes acabam estragando qualquer esforço para implementar um modelo de objeto IoC / DI puro. Ou seja, você não pode criar uma simulação para testá-los e precisa criar dependências reais.

Se a sua empresa / projeto é fortemente investida na IoC, é claro que isso é uma preocupação razoável, e a dor que você está enfrentando é para o ganho de todos. Do ponto de vista arquitetônico, no entanto, eu pessoalmente não acho que qualquer modelo deva ser seguido até o túmulo. Há algumas coisas que fazem sentido implementar usando métodos estáticos - a estratégia de design singleton, por exemplo. Sou menos propenso a classes estáticas, porque acho que elas tendem a perder as vantagens do OO, mas há momentos em que uma classe de biblioteca é provavelmente uma expressão mais natural de algo do que um mecanismo.


O comentarista me lembra os métodos de extensão, que obviamente precisam estar em classes estáticas em C #. Isso é legítimo e é um exemplo de como a IoC pura não funciona muito bem em C #, pelo menos se você estiver tentando tirar proveito da amplitude do idioma.


1
Se uma classe é tornada estática pelos motivos certos e implementada corretamente, como por exemplo, métodos de extensão, ela ainda pode ser testada, pois não teria dependências externas e é autônoma e, como tal, não deve exigir zombaria. A necessidade de uma interface não deve ser direcionada pelo requisito de design para atender melhor a um requisito comercial, mas não para manter um projeto de IoC / DI puro?
Nope

1
Você e eu concordamos com o ponto geral de que você deve escolher a ferramenta certa para o trabalho certo e não ser algemado pela filosofia. Se, no entanto, houver uma cultura bem estabelecida em seu projeto de IoC / DI pura, isso machucará tanto quanto converter uma solução tradicionalmente descendente para IoC / DI. A engenharia precisa considerar os custos de transição. A propósito, não acho que os métodos de extensão o levem até lá. Eu acho que a melhor solução para o comportamento IoCish com classes estáticas seria ter várias classes escolhidos entre em tempo de compilação com as directivas de pré-processamento, estilo C
jwrush

1
Er. Eu sou estúpido. Você quis dizer uma classe estática para os métodos de extensão HOLDING, que, é claro, é como você deve fazê-los. Sim, é claro, você deseja uma classe estática para esse fim. Isso me deixou em dúvida: e isso mostra que o C # não se destinava à IoC. :)
jwrush

Sim, você está certo, se uma cultura existente em um projeto existente existir, seria mais prejudicial do que ajudar a mudar a maneira como as coisas são feitas.
Nope

5

Às vezes, as classes estáticas são mal utilizadas e devem ser:

  • um singleton (em que a classe não estática possui um membro estático e uma variável pública Instance estática para recuperá-lo / criá-lo apenas uma vez.
  • um método em outra classe (onde o estado importa). Se você tem um membro que não está usando dados dessa classe, provavelmente ele não deve fazer parte dessa classe.

Também pode ser uma função chamada 'utilitário' e parte de uma classe de utilitário. As classes de utilitário são classes que contêm apenas métodos estáticos e servem como funções auxiliares sem qualquer contexto.


@topomorto Porque a classe não deve ser instanciada pelo seu construtor, resultando em vários objetos / instâncias, mas por um método especial (normalmente instance ()) <, que retorna sempre a mesma instância, instanciada após a primeira chamada à instância ().
Michel Keijzers

@topomorto Acabei de verificar e você está completamente certo, apenas alguns membros são estáticos (a variável de instância e a função de instância). Vou mudar minha resposta de acordo; obrigado por mencionar.
Michel Keijzers

4

Os métodos estáticos são bons de usar e têm um lugar de direito na programação. Parece que seu arquiteto possui uma estrutura de teste que não suporta totalmente métodos estáticos. Se o seu código faz parte de um projeto maior, é importante atender às diretrizes dos arquitetos. No entanto, não permita que este projeto o impeça de usar métodos estáticos quando for apropriado.


1

Eu tenho usado propriedades estáticas para coisas que são comuns a todas as instâncias de uma classe. E tenho usado métodos estáticos para obter grupos de objetos de classe. Eu não sou um especialista, mas isso tem funcionado para mim até agora.

Exemplo de PHP:

class vendorData {
  private static $data_table = 'vendor_data'; // static property
  private $id; // instance property
  private $name; // instance property

  public function __construct($vendor_id) {
    if(!self::validId($vendor_id) ) { // static method
      return false; // id doesn't exist
    }
    $this->id = $vendor_id; // id has been validated
    $this->getProperties(); // object method
  }

  private static function validId($vendor_id) {
    // send db query to find if the id exists
    // return true/false;
  }

  private function getProperties() { // object method
    $sql = "SELECT * FROM `{self::$data_table}` // using static property
        WHERE `id` = {$this->id}"; // using object instance property
    // get resultset
    foreach($result as $property => $value) {
      $this->$property = $value; // object instance properties all set
    }
  }

  // and here
  public static function getBy($property,$value) { // static method to return object array
    $sql = "SELECT `id` FROM `{self::$data_table}` // using static property
      WHERE `$property` = '$value'";
    // send query, get $ids array
    $obj_array = array();
    foreach($ids as $id) {
      // create array of current class objects using special static keyword
      // meaning of static here is different than in property/method declarations
      $obj_array[$id] = new static($id);
    }
    return $obj_array;
  }
}

1

Eu direi que os princípios que eles adotam estão bem, mas a afirmação (não use estática) pode estar errada. Qual é a cobertura do seu código? se o número for alto e você se sentir confortável com o teste de unidade do seu aplicativo, estará bem. Caso contrário, sugiro revisar o código. Você pode achar que as classes estáticas são a razão ou não, isso dependerá de muitas coisas.


-3

Classes com métodos estáticos abusam dos princípios da OOP. Já não é um POO, é uma programação orientada a classe COP.

Eles raramente têm uma "personalidade" clara (eu uso minha metáfora humana favorita aqui) ou identidade. Então eles não sabem quem são. Esse ponto, por si só, é suficiente para se livrar desse conceito no POO, mas há mais deles.

O próximo é uma consequência do primeiro ponto. Como essas classes não sabem quem são, tendem a ser de grandes a grandes.

Terceiro, seu código é absolutamente insustentável. O uso de métodos estáticos introduz dependências ocultas . Eles aparecem por todo o lugar, e você nunca sabe o que está acontecendo em sua classe. Você não pode ter certeza disso apenas olhando para a assinatura do construtor.

Lenta, mas inevitavelmente, seu código está ficando cada vez menos coeso, pois as dependências ocultas são mal controladas. Eles parecem invisíveis. E é tão fácil adicionar outro! Você não precisa modificar a assinatura de nenhum método. Tudo que você precisa fazer é chamar um método estático. E que código feio se segue ...

Ok, você provavelmente já adivinhou. O acoplamento apertado é um atendente frequente de baixa coesão. Se houver muitos clientes usando alguma classe, o acoplamento está ficando mais apertado. E há uma grande sedução no uso de uma classe ou método já escrito que requer apenas pequenas correções. Apenas um método flag-parameter.

O que usar em vez de métodos estáticos? Bem, minha sugestão é OOP. Por que não tentar?

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.