"Estático" como uma pista semântica sobre apatridia?


11

Recentemente, realizei uma refatoração de um projeto de tamanho médio em Java para voltar e adicionar testes de unidade. Quando percebi como era chato zombar de singletons e estáticos, finalmente "entendi" o que estava lendo sobre eles esse tempo todo. (Eu sou uma daquelas pessoas que precisa aprender com a experiência. Oh, bem.)

Então, agora que estou usando o Spring para criar os objetos e conectá-los, estou me livrando das staticpalavras-chave esquerda e direita. (Se eu pudesse potencialmente zombar, não é realmente estático no mesmo sentido que Math.abs (), certo?) O problema é que eu tinha o hábito de usar staticpara denotar que um método não confiava em qualquer estado do objeto. Por exemplo:

//Before
import com.thirdparty.ThirdPartyLibrary.Thingy;
public class ThirdPartyLibraryWrapper {
    public static Thingy newThingy(InputType input) {
         new Thingy.Builder().withInput(input).alwaysFrobnicate().build();
    }
}

//called as...
ThirdPartyLibraryWrapper.newThingy(input);
//After
public class ThirdPartyFactory {
    public Thingy newThingy(InputType input) {
         new Thingy.Builder().withInput(input).alwaysFrobnicate().build();
    }
}

//called as...
thirdPartyFactoryInstance.newThingy(input);

Então, aqui é onde fica sensível. Gostei da maneira antiga, porque a letra maiúscula me dizia que, assim como Math.sin (x), ThirdPartyLibraryWrapper.newThingy (x) fazia a mesma coisa sempre da mesma maneira. Não há estado de objeto para alterar a maneira como o objeto faz o que estou pedindo. Aqui estão algumas respostas possíveis que estou considerando.

  • Ninguém mais se sente assim, então há algo errado comigo. Talvez eu realmente não tenha internalizado a maneira OO de fazer as coisas! Talvez eu esteja escrevendo em Java, mas pensando em FORTRAN ou algo assim. (O que seria impressionante, pois nunca escrevi FORTRAN.)
  • Talvez eu esteja usando a estática como uma espécie de proxy da imutabilidade para fins de raciocínio sobre código. Dito isto, que pistas devo ter no meu código para alguém aparecer para mantê-lo sabendo o que é stateful e o que não é?
  • Talvez isso deva vir de graça se eu escolher boas metáforas de objetos? por exemplo thingyWrapper, não parece ter um estado independente do empacotado, o Thingyque pode ser mutável. Da mesma forma, thingyFactoryparece que deve ser imutável, mas pode ter estratégias diferentes que são escolhidas na criação.

Respostas:


12

Gostei da maneira antiga, porque a letra maiúscula me dizia que, assim como Math.sin (x), ThirdPartyLibraryWrapper.newThingy (x) fazia a mesma coisa sempre da mesma maneira. Não há estado de objeto para alterar a maneira como o objeto faz o que estou pedindo.

Essa é a maneira certa de fazê-lo, sim. Dito isto, staticnão confere garantias de imutabilidade ou persistência do estado. O uso como sugestão para essas garantias faz sentido, mas não é garantido.

Considere o seguinte:

public static class Logger
{
    public static int LogLevel { get; set; }

    public static void Log(string message, int logLevel)
    {
        if (logLevel >= LogLevel)
        {
            // logs the message, but only if it is important enough.
        }
    }
}

Essa classe não apenas mantém o estado, mas o comportamento do Logger.Log()método muda quando esse estado é alterado. É um exercício perfeitamente legítimo de um staticpadrão de classe, mas não tem nenhuma das garantias semânticas que você está sugerindo.


Eu dei a você a marca de seleção porque você apresenta um contraponto útil à base da minha intuição, mas eu ainda gostaria de ter uma ideia de como devo representar esse contrato / expectativa sobre falta de estado e falta de efeitos colaterais via codificação convenções.
Leo1

1
Normalmente, essas expectativas estão incluídas na documentação do método; o mesmo se aplica à segurança da linha.
Robert Harvey

5

Na minha opinião, o que você está dizendo - todas as funções estáticas são nulipotentes - é uma boa maneira OO de escrever código na maioria dos casos, mas não acredito que, quando você olha para uma função estática, deve assumir automaticamente que ela não tem efeitos colaterais. . A situação pode ser mais complexa; por exemplo, a função Class.forName(String)que parece sem estado, mas na verdade carrega uma classe na memória e, eventualmente, instancia campos estáticos / executa o inicializador estático; este é um exemplo de função idempotente (chamadas eventuais após a primeira não fazem diferença), mas não é uma função pura (não se pode dizer que não possui efeitos colaterais). Essa também é uma boa escolha, mas pode haver casos em que três chamadas distintas para a mesma função gerem três resultados diferentes; por exemplo, se você ligarThread.currentThread() em um aplicativo multithread, não há garantia de que você receberá o mesmo thread toda vez.

Dito isto, que pistas devo ter no meu código para alguém aparecer para mantê-lo sabendo o que é stateful e o que não é?

Uma solução adequada é documentar (Javadoc, por exemplo); além disso, a partir do nome da função e do que ela faz às vezes, pode-se inferir que a função estática é uma função pura. Por exemplo, não há razão para alguém acreditar que o Assert.assertTrue(boolean)JUnit mudaria algum estado em algum lugar. Por outro lado, quando uma função como System.clearProperty(String)é chamada, é bastante óbvio que haverá efeitos colaterais.


Qual é a diferença entre "nullipotent" e "stateless"?
21715 Pacerier

@ Pacerier Não deve haver nenhuma diferença.
M3th0dman 2/11

4

Dito isto, que pistas devo ter no meu código para alguém aparecer para mantê-lo sabendo o que é stateful e o que não é?

Você pode torná-los estáticos. O FxCop no mundo C # pressionará você a tornar estáticas as coisas que não fazem referência a variáveis-membro. Esta é a coisa correta a se fazer (geralmente).

Quando percebi como era chato zombar de singletons e estáticos, finalmente "entendi" o que estava lendo sobre eles esse tempo todo.

Mover todos eles para instâncias talvez não seja a abordagem correta. Em vez disso, desacople as dependências dos métodos estáticos das classes que os usam. Você pode testar os métodos estáticos isoladamente e, em seguida, substituir a dependência dos consumidores quando eles precisam ser testados.


Você pode me mostrar um exemplo de "dissociando as dependências de métodos estáticos"? Não está claro para mim o que você está ativando. Eu já escondi os detalhes da implementação em um método estático. No final do dia, a classe que a usa precisa alcançar essa funcionalidade. Você está dizendo que devo criar um objeto de instância que tenha invólucros finos para todos os métodos estáticos?
leoger

1
@leoger - a classe que usa o método estático não precisa alcançar a funcionalidade se você estiver testando essa classe, caso contrário você não estaria zombando do método estático.
Telastyn
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.