Por que e quando devo tornar a classe 'estática'? Qual é o objetivo da palavra-chave 'estática' nas classes?

47

A staticpalavra-chave em um membro em vários idiomas significa que você não deve criar uma instância dessa classe para poder ter acesso a esse membro. No entanto, não vejo justificativa para fazer uma aula inteira static. Por que e quando devo fazer uma aula static?

Quais são os benefícios que recebo ao fazer uma aula static? Quero dizer, depois de declarar uma classe estática, ainda devemos declarar todos os membros aos quais ele / ela deseja acessar sem instanciação, como estáticos também.

Isso significa que, por exemplo, a Mathclasse pode ser declarada normal (não estática), sem afetar o modo como os desenvolvedores codificam. Em outras palavras, tornar uma classe estática ou normal é transparente para os desenvolvedores.

Saeed Neamati
fonte
Se você estiver codificando no estilo Func Prog à Rich Hickey, isso pode ser útil.
Job
1
Na minha opinião, as classes estáticas são apenas uma muleta através da qual o C # tenta ocultar uma falha de design maciça na linguagem. Por que estruturar uma linguagem em torno dessa ideologia absurda de que tudo tem que ser uma classe, apenas para introduzir uma sintaxe adicional para que você possa trapacear e contornar essa restrição desnecessária. Se o C # tivesse permitido funções livres desde o início, não haveria necessidade de classes estáticas.
Antred 07/12/16

Respostas:

32

Isso torna óbvio para os usuários como a classe é usada. Por exemplo, seria absurdo completo escrever o seguinte código:

Math m = new Math();

O C # não precisa proibir isso, mas, como não serve para nada, é melhor dizer isso ao usuário. Certas pessoas (inclusive eu) aderem à filosofia de que as linguagens de programação (e APIs ...) devem ser o mais restritivas possível para dificultar o uso errado: as únicas operações permitidas são aquelas que são significativas e (espero) corretas.

Konrad Rudolph
fonte
3
Eu discordo dessa filosofia. O problema que tenho é que precisa mudar. Portanto, embora possa ter feito sentido restringi-lo antes, essa restrição dificulta nosso desenvolvimento para necessidades futuras. E a biblioteca herdada que somos obrigados a usar para fazer interface com o sistema antigo (aquele que você escreveu, selou e classificou todas as classes) não fornece nenhuma maneira de estender ou modificar o código que você escreveu. Só porque você não tinha motivos para instanciar sua classe, não significa que não precisarei disso no futuro. Enquanto a classificação de uma classe de utilidade faz sentido, considere o futuro também.
SoylentGray
19
@Chad Então a biblioteca está mal projetada. Em última análise, estender classes que não foram projetadas para extensão não funciona; portanto, é melhor criar a classe sealedem primeiro lugar. Eu reconheço que nem todas as pessoas concordam com esse sentimento, mas os comentários não são um bom lugar para discutir isso. Mas, no caso de staticeste problema não até mesmo representar em si: instanciar System.Mathvai não faz sentido.
Konrad Rudolph
1
@ Konard, se você declarar todos os membros da Mathclasse como estático sem declarar a Mathclasse como estática, ainda poderá usá-lo, sem necessidade de instanciação.
Saeed Neamati
11
@Saeed - Isso é verdade, mas você também pode instanciar a classe, o que não serve a nenhum propósito. O que faço com uma instância da classe Math? Para mostrar explicitamente a intenção da classe (nenhuma instanciação é necessária ou desejada), está marcada static.
março de Corbin
3
@Saeed Vamos mudar isso: proponho que você leia minha resposta novamente, pois você parece ter entendido errado : seus comentários não estão relacionados à minha resposta. Eu nunca disse que você precisa escrever new Math().
Konrad Rudolph
24

O StackOverflow tem uma ótima discussão sobre esse tópico . Para facilitar a referência, copio e colo aqui, em nome de seu autor, Mark S. Rasmussen :

Eu escrevi meus pensamentos sobre classes estáticas em um tópico anterior :

Eu adorava classes utilitárias preenchidas com métodos estáticos. Eles fizeram uma grande consolidação de métodos auxiliares que, de outra forma, ficariam por aí causando redundância e um inferno de manutenção. Eles são muito fáceis de usar, sem instanciação, sem descarte, apenas esquecem. Eu acho que essa foi minha primeira tentativa involuntária de criar uma arquitetura orientada a serviços - muitos serviços sem estado que apenas fizeram seu trabalho e nada mais. À medida que o sistema cresce, os dragões estão chegando.

Polimorfismo

Digamos que temos o método UtilityClass.SomeMethod que felizmente vibra. De repente, precisamos alterar ligeiramente a funcionalidade. A maior parte da funcionalidade é a mesma, mas precisamos alterar algumas partes. Se não fosse um método estático, poderíamos criar uma classe derivada e alterar o conteúdo do método conforme necessário. Como é um método estático, não podemos. Claro, se precisarmos adicionar funcionalidades antes ou depois do método antigo, podemos criar uma nova classe e chamar a antiga dentro dela - mas isso é nojento.

Problemas na interface

Métodos estáticos não podem ser definidos por meio de interfaces por razões lógicas. E como não podemos substituir métodos estáticos, as classes estáticas são inúteis quando precisamos transmiti-las pela interface. Isso nos torna incapazes de usar classes estáticas como parte de um padrão de estratégia. Podemos corrigir alguns problemas passando delegados em vez de interfaces.

Testando

Isso basicamente anda de mãos dadas com os problemas de interface mencionados acima. Como nossa capacidade de trocar implementações é muito limitada, também teremos problemas para substituir o código de produção pelo código de teste. Novamente, podemos agrupá-los, mas isso exigirá que alteremos grandes partes do nosso código apenas para poder aceitar wrappers em vez dos objetos reais.

Promove blobs

Como métodos estáticos são geralmente usados, como métodos de utilidade e métodos de utilidade geralmente têm finalidades diferentes, acabaremos rapidamente com uma classe grande preenchida com funcionalidade não coerente - idealmente, cada classe deve ter uma única finalidade no sistema. Prefiro ter cinco vezes mais aulas, desde que seus objetivos sejam bem definidos.

Creep do parâmetro

Para começar, esse método estático bonitinho e inocente pode ter um único parâmetro. À medida que a funcionalidade cresce, alguns novos parâmetros são adicionados. Em breve, são adicionados parâmetros opcionais, portanto criamos sobrecargas do método (ou apenas adicionamos valores padrão, nos idiomas que os suportam). Em pouco tempo, temos um método que leva 10 parâmetros. Somente os três primeiros são realmente necessários, os parâmetros 4-7 são opcionais. Mas se o parâmetro 6 for especificado, é necessário preencher também de 7 a 9 ... Se tivéssemos criado uma classe com o único objetivo de fazer o que esse método estático fazia, poderíamos resolver isso incorporando os parâmetros necessários no construtor e permitindo que o usuário defina valores opcionais por meio de propriedades ou métodos para definir vários valores interdependentes ao mesmo tempo. Além disso,

Exigir que os consumidores criem uma instância de classes sem motivo

Um dos argumentos mais comuns é: por que exigir que os consumidores de nossa classe criem uma instância para invocar esse método único, sem ter utilidade para a instância posteriormente? Criar uma instância de uma classe é uma operação muito barata na maioria dos idiomas; portanto, a velocidade não é um problema. Adicionar uma linha extra de código ao consumidor é um custo baixo para estabelecer as bases de uma solução muito mais sustentável no futuro. E, finalmente, se você deseja evitar a criação de instâncias, basta criar um wrapper singleton de sua classe que permita reutilização fácil - embora isso exija a exigência de que sua classe não tenha estado. Se não for apátrida, você ainda pode criar métodos de empacotamento estático que lidam com tudo, enquanto oferece todos os benefícios a longo prazo. Finalmente,

Apenas um Sith lida com absolutos

Obviamente, há exceções ao meu desagrado por métodos estáticos. As verdadeiras classes de utilidade que não apresentam nenhum risco de inchaço são excelentes casos para métodos estáticos - System.Convert como exemplo. Se o seu projeto é único, sem requisitos para manutenção futura, a arquitetura geral realmente não é muito importante - estática ou não estática, não importa - a velocidade de desenvolvimento, no entanto.

Padrões, normas, normas!

O uso de métodos de instância não o impede de usar métodos estáticos e vice-versa. Desde que haja um raciocínio por trás da diferenciação e seja padronizado. Não há nada pior do que examinar uma camada de negócios que se estende a diferentes métodos de implementação.

Rick
fonte
12
Hmmm, não sei se copio e colo uma resposta inteira aqui. Talvez o link e a paráfrase, incluam um ponto de vista alternativo, pois você menciona uma "discussão", compartilha sua própria experiência?
março de Corbin
2
"Apenas um Sith lida com absolutos" - eu nunca tinha visto isso usado assim antes ... mas é PERFEITO. Me fez rir.
Brook
4
@ Corbin: Mark teve uma ótima resposta. Dei crédito a ele e copiei / colei para facilitar a referência. Minhas próprias opiniões sobre esse assunto coincidem com as de Mark. Não vejo o objetivo do seu comentário, a não ser começar o debate.
21711 Rick
1
@ Rick: A resposta copiada é bastante longa, e parafrasear definitivamente ajudaria. Apenas uma frase como "Não é apenas que eles não sejam realmente úteis, eles podem até prejudicar, como mostra este post do SO:" me deixaria ignorá-lo rapidamente, pois vejo que não é a informação que eu estava procurando.
blubb
@ Simon: LOL + 1 'ed seu post. Na tentativa de parar de discutir sobre algo tão estúpido, eu simplesmente concordo. :) Espero que o pôster original tenha achado útil minha resposta / cópia e colagem.
Rick
16

Estou surpreso que ninguém mais tenha mencionado que as staticclasses permitem métodos de extensão - estendendo com segurança um tipo existente (incluindo a adição de definições de métodos às interfaces ) que você não possui. Por exemplo, o que em Scala é

trait MyFoo {
  def foo: Int
  def plusFoo(a: Int) = foo + a
}

pode ser expresso em C # como

public interface IMyFoo {
  int Foo();
}

public static class MyFooExtensions {
  public static int PlusFoo(this IMyFoo f, int a) {
    return f.Foo() + a;
  }
}
Frank Shearar
fonte
você encontrou agulha no palheiro. +1
Saeed Neamati
3

Para mim, uma classe estática (em C #) é como chamar uma função.

Por exemplo

public static string DoSomething(string DoSomethingWithThisString){}

Passo uma corda e volto uma corda. Tudo está contido nesse método. Sem acesso à variável membro, etc.

Honestamente, principalmente para mim, tem sido diminuir as linhas de código:

Por que fazer:

MyClass class = new MyClass();
String s = class.DoSomething("test");

Quando eu posso fazer isso:

String s = MyClass.DoSomething("test");

Então, eu usaria classes estáticas nesses casos, quando quisesse fazer algo sem a necessidade de uma classe e significaria menos digitação.

Os pontos Mark S. são válidos, mas para mim, se eu tiver alguns métodos utilitários, é mais fácil fingir que estou chamando uma função para referenciá-los como liners únicos.

Isso pode ser simplista, mas é principalmente assim que eu tenho usado static.

Jon Raynor
fonte
1
novo MyClass (). DoSomething ("test") ainda é válido. Eu uso muito isso para Test Data Builders.
JoJoComasFdz 4/14