Os conselhos conflitantes que você leu todos fazem sentido de alguma maneira, mas todos eles fazem suposições (diferentes) ou frases ambíguas. É um daqueles "dizendo a mesma coisa com palavras diferentes" tipos de distinções.
um método só deve ser estático se não modificar um estado
Observe a ambiguidade de "modificar um estado". O exemplo a seguir viola essa regra (literalmente), mas preserva o espírito (figurativo) da regra:
public static void SetUserNameToBob(User u)
{
u.Name = "Bob";
}
Isso modifica o estado do User
objeto e, de fato, "modifica um estado".
No entanto, esse método não depende de um estado interno específico para decidir seu próprio fluxo lógico (por exemplo, u.Name = currentlySelectedDefaultName()
seria uma violação, pois o nome selecionado é um estado selecionado). E acho que é isso que significa: nenhum estado interno é modificado.
[um método só deve ser estático se] seu resultado depende apenas dos parâmetros fornecidos a ele
Veja o item anterior, este está praticamente dizendo a mesma coisa. O que isso significa é que isto:
public static string currentlySelectedDefaultName;
public static void SetUserNameToBob(User u)
{
u.Name = currentlySelectedDefaultName;
}
não deve ser estático, pois o nome "padrão atual" é um estado e, portanto, não deve ser uma variável / método global.
Pense no que acontece se dois threads separados estiverem em execução: um deles quer usar o padrão "Bob", o outro quer usar o padrão "Jim". Eles acabarão brigando pelo valor, que pode criar problemas de depuração maciços e comportamento inesperado.
Se, no entanto, cada thread tiver seu próprio DefaultNameSetter
objeto, eles não lutarão pelo mesmo recurso.
No entanto, a resposta mais votada nesta publicação afirma que métodos estáticos devem ser usados sempre que possível.
Isso é meio que impor uma regra por tentativa / falha. Podemos definir esse método como estático?
- Sim
=>
bom! Mantenha assim!
- Não
=>
Isso prova que o código depende de um valor não estático em algum lugar e, portanto, não deve ser tornado estático.
Para citar indiretamente Jeff Goldblum em Jurassic Park , você não deve discutir a necessidade de fazer algo apenas provando que isso pode ser feito.
Novamente, a abordagem não está necessariamente (ou sempre) errada, mas assume cegamente que os métodos já foram escritos para serem tão independentes do estado quanto logicamente possível, o que simplesmente não é o caso.
Mesmo se você se inscrever nessa abordagem metodológica, ainda poderá aplicá-la quando um projeto não estiver mais em desenvolvimento. Se o projeto ainda estiver em desenvolvimento, os métodos atuais podem ser espaços reservados para a futura implementação. Pode ser possível tornar Foo()
estática hoje, mas não amanhã, se a lógica dependente do estado simplesmente ainda não tiver sido implementada.
Muitas das respostas neste post dizem que é preciso fazer o que for mais lógico.
Bem, eles não estão errados; mas isso não é apenas uma pequena reformulação de dizer "faça a coisa certa"? Esse não é um conselho realmente útil, a menos que você já saiba quando usar estática e quando evitá-la. É uma pegadinha 22.
Então, quando você deve usar estática?
Como você notou, nem todos concordam com a regra, ou pelo menos em como formular a regra. Vou adicionar outra tentativa aqui, mas esteja ciente de que isso está criando efetivamente outro padrão :
Tenha isso em mente.
A estática são verdades universais.
Esse é o objetivo de um espaço para nome global: coisas que estão corretas em toda a camada do aplicativo.
Há um argumento de declive escorregadio aqui. Alguns exemplos:
var myConfigKey = ConfigurationManager.AppSettings["myConfigKey"];
Este é um exemplo muito claro. A configuração do aplicativo implica inerentemente que a configuração é global para o aplicativo e, portanto, é garantido um método estatístico.
bool datesOverlap = DateHelper.HasOverlap(myDateA_Start, myDateA_End, myDateB_Start, myDateB_End);
Este método é universalmente correto. Não importa quais datas você está comparando. O método não se importa com o significado das datas. Sejam datas de emprego, datas de contrato, ... não importa para o algoritmo do método.
Observe a semelhança semântica entre contextual e guiada pelo estado . Ambos os tipos se referem a um estado "não universal". Isso prova que as diferenças contextuais, portanto, dependem do estado e não são adequadas para serem tornadas estáticas.
var newPersonObject = Person.Create();
Esta é uma verdade universal. O mesmo processo de criação é usado em todo o aplicativo.
No entanto, esta linha pode ficar embaçada:
var newManager = Person.CreateManager();
var newJanitor = Person.CreateJanitor();
Do ponto de vista técnico, nada mudou. Gerente (e zeladores) são criados da mesma maneira em todo o aplicativo. No entanto, isso criou sutilmente um estado (gerente / zelador), que diminui lenta mas firmemente o quão universal é realmente a verdade.
Isso pode ser feito? Do ponto de vista técnico; sim .
Isso deveria ser feito? É uma questão de saber se você está discutindo o princípio puro ou se leva em consideração que é necessário fazer compromissos para não se esforçar sem sentido pela perfeição lógica. Então, eu diria que se não criar um problema maior do que o problema pretende resolver .
À medida que as opções se expandem (gerentes, zeladores, contadores, vendedores, ...), o problema aumenta. Para um problema suficientemente grande, é desejável um padrão de fábrica .
Se você tiver apenas duas opções e não tiver motivos para suspeitar que a lista de opções aumentará, é possível argumentar que os métodos de criação estáticos são suficientes. Alguns podem discordar, e eu também entendo o que eles querem dizer. Mas eu costumo ser prático e não excessivamente perfeccionista em minha abordagem.