Não posso simplesmente usar todos os métodos estáticos?

64

Qual é a diferença entre os dois métodos UpdateSubject abaixo? Eu senti que usar métodos estáticos é melhor se você apenas deseja operar com as entidades. Em quais situações devo usar métodos não estáticos?

public class Subject
{
    public int Id {get; set;}
    public string Name { get; set; }

    public static bool UpdateSubject(Subject subject)
    {
        //Do something and return result
        return true;
    }
    public bool UpdateSubject()
    {
        //Do something on 'this' and return result
        return true;
    }
}

Eu sei que vou receber muitos chutes da comunidade por essa pergunta realmente irritante, mas não consegui parar de perguntar.

Isso se torna impraticável quando se trata de herança?

Atualização:
Está acontecendo em nosso local de trabalho agora. Estamos trabalhando em um aplicativo da web asp.net de 6 meses com 5 desenvolvedores. Nosso arquiteto decidiu usar todos os métodos estáticos para todas as APIs. Seu raciocínio como métodos estáticos é leve e beneficia os aplicativos da web, mantendo a carga do servidor baixa.

Alexander
fonte
9
Rich Hickey encorajaria algo não-idiomático assim (todos os métodos estáticos). Se você gosta de um estilo funcional, também pode usar uma linguagem funcional;) ​​Nem tudo no software é melhor modelado como um objeto.
Job
13
@ Job: Eu realmente não vejo como isso é funcional - é processual.
Aaronaught 3/08/11
2
@ Job: Esta classe não é imutável.
Aaronaught 4/08/11
3
@DonalFellows: Claro que você pode, C # e F # são paradigmas mistos. Mas o fato permanece: métodos estáticos! = Programação funcional. FP significa funções de passagem / encadeamento, tipos de dados imutáveis, prevenção de efeitos colaterais etc. Isso é completamente o oposto do que o trecho de código no OP faz.
Aaronaught 22/01
4
"Seu raciocínio como métodos estáticos é leve e beneficia os aplicativos da web, mantendo a carga do servidor baixa". Isso esta errado. Mantendo o servidor carregado?
mamcx 01/09

Respostas:

87

Vou abordar os problemas mais óbvios dos métodos estáticos com parâmetros explícitos "this":

  1. Você perde o despacho virtual e, posteriormente, o polimorfismo. Você nunca pode substituir esse método em uma classe derivada. É claro que você pode declarar um método new( static) em uma classe derivada, mas qualquer código que o acessa deve estar ciente de toda a hierarquia de classes e fazer verificação e conversão explícitas, que é precisamente o que o OO deve evitar .

  2. Como uma extensão nº 1, não é possível substituir instâncias da classe por uma interface , porque as interfaces (na maioria dos idiomas) não podem declarar staticmétodos.

  3. A verbosidade desnecessária. O que é mais legível: Subject.Update(subject)ou apenas subject.Update()?

  4. Verificação de argumento. Novamente depende da linguagem, mas muitos compilarão uma verificação implícita para garantir que o thisargumento não seja nullpara impedir que um bug de referência nulo crie condições inseguras de tempo de execução (tipo de saturação de buffer). Não usando métodos de instância, você teria que adicionar essa verificação explicitamente no início de cada método.

  5. É confuso. Quando um programador normal e razoável vê um staticmétodo, ele naturalmente assume que não requer uma instância válida (a menos que instale várias instâncias, como um método de comparação ou igualdade, ou se possa operar com nullreferências) . Ver métodos estáticos usados ​​dessa maneira nos fará fazer uma captura dupla ou tripla, e após a quarta ou quinta vez estaremos estressados ​​e com raiva, e que Deus o ajude se soubermos o seu endereço residencial.

  6. É uma forma de duplicação. O que realmente acontece quando você chama um método de instância é que o compilador ou o tempo de execução consulta o método na tabela de métodos do tipo e o invoca usando thiscomo argumento. Você está basicamente reimplementando o que o compilador já faz. Você está violando DRY , repetindo o mesmo parâmetro repetidamente em métodos diferentes quando não é necessário.

É difícil conceber qualquer boa razão para substituir métodos de instância por métodos estáticos. Por favor não.

Aaronaught
fonte
5
+1 apenas para sua linha final. Eu gostaria que muito mais pessoas pensassem em "método de instância" antes de pensarem em "método estático".
Stuart Leyland-Cole
4
+1. Eu acrescentaria que os métodos estáticos tornam difícil a impossibilidade de escrever testes, pois na maioria dos casos você não pode zombar deles: uma vez que qualquer parte do seu código depende de um método estático, você não pode substituí-lo por um op em seus testes. Um bom exemplo no mundo do .NET seria classes preferem File, FileInfoe DirectoryInfo(e, de fato, existem bibliotecas que eles se escondem atrás de interfaces que podem então ser injetados conforme necessário e ridicularizado em testes).
sm
Eu acho que seus pontos de vista sobre polimorfismo / herança são discutíveis. não se deve usar herança para reutilização de código, portanto isso é irrelevante. a parte da herança também é duvidosa. na maioria das linguagens modernas, as assinaturas de métodos são polimórficas em si mesmas. se você adota uma abordagem funcional, começa a repassar métodos e compõe métodos complexos a partir de métodos simples que são intercambiáveis ​​com base em sua interface. basicamente, não há desvantagens para métodos estáticos que não são compensados ​​simplesmente projetando com eles em mente, o mesmo que com OOP.
Sara
@ SM simplesmente não é verdade. se você tem uma forte dependência de QUALQUER COISA, estática ou não, que não pode estar em um teste de unidade, então é um período não testável. estático não causa isso. um método estático pode ser injetado da mesma maneira que uma classe que representa uma conexão db. você está assumindo que "estático" significa "estática mais dependências ocultas que afetam o estado global e os recursos externos". isso não é justo.
sara
@kai tente injetar um método estático que faça Y em vez de X, sejam eles X e Y. Você não precisa depender de nada externo para ser ferrado. Talvez você esteja interessado apenas em testar algum aspecto que não exija um cálculo demorado X para ser executado. Como injetar seu método estático sofisticado sem criar um wrapper? A passagem de funções não é suportada por todos os idiomas. Os métodos estáticos têm seus casos de uso e eu os uso quando faz sentido . Este é, como sempre acontece, um caso de "apenas porque você pode não significa necessariamente que deveria".
sm
13

Você descreveu exatamente como fazer OOP em C, no qual apenas métodos estáticos estão disponíveis, e "this" é um ponteiro de estrutura. E sim, você pode executar o polimorfismo do tempo de execução em C especificando um ponteiro de função durante a construção para ser armazenado como um elemento da estrutura. Os métodos de instância disponíveis em outros idiomas são apenas açúcar sintático que essencialmente faz exatamente a mesma coisa sob o capô. Algumas linguagens, como python, estão no meio do caminho, onde o parâmetro "self" está explicitamente listado, mas uma sintaxe especial para chamá-lo lida com herança e polimorfismo para você.

Karl Bielefeldt
fonte
FWIW, com C, você pode fazer o despacho virtual assim: obj->type->mthd(obj, ...);e isso é substancialmente semelhante ao que acontece com o despacho virtual em outros idiomas (mas nos bastidores).
Donal Fellows
@Karl Você pode fornecer algum tipo de referência por escrito para começar a ir mais fundo?
Jorge Lavín
Você pode considerar o GObject como uma boa implementação de referência.
Karl Bielefeldt
10

O problema com o método estático ocorre no momento em que você precisa de uma subclasse.

Os métodos estáticos não podem ser substituídos nas subclasses; portanto, suas novas classes não podem fornecer novas implementações dos métodos, tornando-os menos úteis.


fonte
2
Isso não é necessariamente um problema. Algumas linguagens fornecem outros mecanismos (métodos não virtuais em C ++ e C #) para realizar a mesma coisa intencionalmente - o C # até fornece métodos "selados" para estender ainda mais essa idéia! Obviamente, você não faria isso apenas para os pedaços dele, mas é uma boa técnica para estar ciente de ...
Shog9
@ Steve, isso não está substituindo, pois você ainda pode chamar os dois métodos diretamente.
@ Steve, em métodos estáticos Java não pode ser substituído.
@ Thorbjørn - primeiro, a pergunta não é marcada como Java ou qualquer outra linguagem OOP específica. Mais importante, eu não disse que você poderia substituir uma função de membro estática. Eu estava tentando usar uma analogia para fazer uma observação. Como eu claramente falhei, os comentários foram excluídos.
precisa saber é o seguinte
2
Em certo sentido, essas ainda são "novas implementações de métodos". Apenas não no sentido OOP padrão. Este se sente um pouco como eu estou dizendo "o seu texto não é perfeito, quer, nur-nur-na-nur-nur" ;-)
Steve314
7

Se você declarar um método static, não precisará instanciar um objeto da classe (usando a newpalavra - chave) para executar o método. No entanto, você não pode se referir a nenhuma variável de membro, a menos que elas também sejam estáticas; nesse caso, essas variáveis ​​de membro pertencem à classe, não um objeto instanciado específico da classe.

Em outras staticpalavras , a palavra - chave faz com que uma declaração faça parte da definição da classe, tornando-se um único ponto de referência em todas as instâncias da classe (objetos instanciados a partir da classe).

Conclui-se, então, que se você deseja várias cópias em execução da sua classe e não deseja que o estado (variáveis-membro) seja compartilhado entre todas as suas cópias em execução (ou seja, cada objeto possui seu próprio estado exclusivo), então você não pode declarar essas variáveis ​​de membro estáticas.

Em geral, você deve usar staticclasses e métodos somente ao criar métodos utilitários que aceitem um ou mais parâmetros e retornar um objeto de algum tipo, sem efeitos colaterais (ou seja, alterações nas variáveis ​​de estado na classe)

Robert Harvey
fonte
11
Eu acho que o OP entende o que staticsignifica, ele está perguntando por que não declarar tudo como estático.
Ed S.
11
Ele está passando em uma instância de Subject- mas como um parâmetro explícito, em vez do thisparâmetro implícito .
precisa saber é o seguinte
Bem, sim ... mas eu não entendo o seu ponto, suponho.
Ed S.
@ Ed - apenas que você pode fazer praticamente qualquer coisa através de um parâmetro explícito que você poderia através de um thisparâmetro implícito . Há diferenças nas expectativas, mas fora da herança, não há diferença real no que você pode fazer. Por exemplo, esses membros não estáticos podem ser acessados ​​- através do parâmetro explícito.
Steve314
Eu estava operando sob a suposição de que não era possível acessar membros privados do argumento em C # mesmo por meio de um método estático declarado na mesma classe (como você pode em C ++). Não sei por que pensei isso, mas estava errado.
Ed3 de
3

A razão pela qual as pessoas argumentam que 'os métodos estáticos mostram um design ruim' não é necessariamente devido aos métodos, na verdade são dados estáticos, implícitos ou explícitos. Por implícito, quero dizer QUALQUER estado do programa que não esteja contido nos valores ou parâmetros de retorno. Modificar o estado em uma função estática é um retrocesso para a programação procedural. No entanto, se a função não modifica o estado ou delega a modificação do estado nas funções do objeto componente, na verdade, é mais um paradigma funcional do que processual.

Se você não está mudando de estado, métodos e classes estáticos podem ter muitos benefícios. Torna muito mais fácil garantir que um método seja puro e, portanto, livre de efeitos colaterais e quaisquer erros sejam totalmente reprodutíveis. Ele também garante que métodos diferentes em vários aplicativos usando uma biblioteca compartilhada possam ter certeza de que obterão os mesmos resultados com a mesma entrada, facilitando muito o rastreamento de erros e o teste de unidade.

Por fim, não há diferença na prática entre objetos de grandes dimensões comumente vistos como 'StateManager' e dados estáticos ou globais. O benefício dos métodos estáticos usados ​​corretamente é que eles podem indicar a intenção do autor de não modificar o estado para revisores posteriores.

Mathieson
fonte
11
Sua afirmação "... é um retrocesso para a programação procedural ..." me faz pensar que você a considera tão ruim, acho que faz parte do OO em certo sentido.
NoChance
making ... unit testing much easier.Não, não vai. Isso torna impossível qualquer código que chame métodos estáticos para teste de unidade , pois você não pode isolá-lo do método estático. Uma chamada para um método estático se torna uma dependência codificada para essa classe.
StuperUser
2

Dependendo do idioma e do que você está tentando fazer, a resposta pode ser que não há muita diferença, mas a staticversão tem um pouco mais de confusão.

Como seu exemplo de sintaxe sugere Java ou C #, acho que Thorbjørn Ravn Andersen está certo ao apontar o problema principal (com ligação tardia). Com um método estático, não há parâmetro "especial" para ligação tardia com base, portanto você não pode ter ligação tardia.

De fato, um método estático é apenas uma função em um módulo, esse módulo tendo o mesmo nome que a classe - não é realmente um método OOP.

Steve314
fonte
2

Você está basicamente derrotando todo o objetivo dos objetos quando faz isso. Há uma boa razão para termos mudado de código processual para código orientado a objetos.

Eu nunca declararia um método estático que tomou o tipo de classe como parâmetro. Isso apenas torna o código mais complexo, sem nenhum ganho.

Loren Pechtel
fonte
3
Você pode fazer isso se estiver passando um objectpara um staticmétodo e retornando um novo object. Programadores funcionais fazem isso o tempo todo.
Robert Harvey
pergunta: você considera a Mathclasse estática "complexa para nenhum ganho" ou prefere que todos os tipos de números possuam métodos virtuais que definam valor absoluto, negação, adição, quadratura, raízes arbitrárias e assim por diante? Acho que não. os objetos são organizados quando você precisa armazenar um estado mutável oculto no qual você precisa impor alguns invariantes. juntando todos os métodos visíveis que operam em um tipo no próprio tipo, isso é algo que leva a um código complexo e inatingível. Eu concordo com Robert, você pode querer ver como os programadores funcionais funcionam, isso pode ser realmente uma surpresa!
Sara
@ kai Como eu disse, praticamente. A classe Math é uma exceção razoável, embora anexá-la aos tipos numéricos não seria uma má idéia.
Loren Pechtel
2

Há muitas ótimas respostas aqui, e elas são muito mais perspicazes e informadas do que qualquer outra coisa que eu possa ter como resposta, mas sinto que há algo que não está sendo abordado:

"Nosso arquiteto decidiu usar todos os métodos estáticos para todas as APIs. Seu raciocínio é que os métodos estáticos são leves e beneficiam os aplicativos da web, mantendo a carga do servidor baixa ".

(ênfase em negrito é minha)

Meu 2c nesta parte da pergunta é o seguinte:

Teoricamente, o que se diz é verdade: chamar um método estático só tem a sobrecarga de realmente invocar esse método. Chamar um método não estático (instância) tem a sobrecarga extra de instanciar o objeto primeiro e, em algum momento, destruir a instância (manualmente ou por meio de alguma forma de coleta automática de lixo, dependendo da plataforma usada).

A peça é um pouco do advogado do diabo sobre isso: podemos ir ainda mais longe e dizer coisas como:

  • isso pode se tornar muito ruim se uma instância for criada para cada chamada do método (instance) (em vez de apenas deixá-lo estático e chamá-lo assim)

  • dependendo da complexidade do construtor, da hierarquia de tipos, de outros membros da instância e de outros fatores desconhecidos, a sobrecarga extra da chamada de método não estático pode variar e se tornar realmente grande

Na verdade, IMHO, os pontos acima (e a abordagem geral de "vamos usar a estática porque são mais rápidos") são argumentos / avaliações:

  • se o código for bom e, mais específico a esse argumento, se as instâncias forem criadas apenas quando necessário (e destruídas da melhor maneira possível), você não terá nenhuma sobrecarga extra ao usar métodos de seguro quando apropriado (porque se você apenas crie os objetos necessários, eles teriam sido criados mesmo que esse método fosse declarado estático em algum outro local = mesma sobrecarga de instanciação)

  • abusando da declaração de métodos estáticos dessa maneira, é possível ocultar alguns problemas com seu código em relação à forma como as instâncias são criadas (como o método é estático, um código de instanciação incorreto pode passar despercebido até mais tarde e isso nunca é bom).

Shivan Dragon
fonte
2

Essa é uma pergunta muito interessante que tende a ter respostas muito diferentes, dependendo da comunidade que você está fazendo. Outras respostas parecem ser c # ou viés java: uma linguagem de programação que é (principalmente) pura orientada a objetos e tem um tipo de visão idiomática do que é orientação a objetos.

Em outras comunidades, como C ++, a orientação a objetos é interpretada com mais liberalidade. Alguns especialistas bem conhecidos estudam o assunto e, na verdade, concluem que funções livres melhoram o encapsulamento (a ênfase é minha):

Vimos agora que uma maneira razoável de medir a quantidade de encapsulamento em uma classe é contar o número de funções que podem ser quebradas se a implementação da classe mudar. Sendo esse o caso, fica claro que uma classe com n funções-membro é mais encapsulada do que uma classe com n-1 funções-membro. E essa observação é o que justifica meu argumento para preferir funções de não-membro não-amigo a funções de membro

Scott Meyers

Para aqueles que não estão familiarizados com a terminologia C ++:

  • função membro = método
  • função não membro = método estático
  • não amigo = use apenas API pública
  • função não-membro não-amigo = método estático que usa apenas API pública

Agora, para responder à sua pergunta:

Não posso simplesmente usar todos os métodos estáticos?

Não, você nem sempre deve usar métodos estáticos.

Mas você deve usá-los sempre que precisar usar a API pública de uma classe.

authchir
fonte
-3

em Java ...

Os métodos estáticos não são substituídos, mas ocultos; portanto, seria uma grande diferença (problema?) No caso de estender a classe.

Por outro lado, notei que os programadores Java tendem a pensar que tudo TEM QUE SER objeto, sem realmente entender o porquê, e eu discordo.

Static deve ser usado para código específico para a classe. Static não funciona bem com herança.

alguns bons exemplos de uso de métodos estáticos são funções independentes, sem efeitos colaterais.

veja também a palavra-chave sincronizada ...

Nicolas
fonte
2
Como esse método estático está oculto e onde essa diferença e problema surgiriam ao estender a classe? Como a sincronização se encaixa nisso? Onde os problemas de estática e herança são encontrados? Você pode fornecer métodos estáticos bons e ruins nas bibliotecas Java principais e explicar por que cada um deles é o caso?