Um tipo singleton poderia substituir métodos e classes estáticos? [duplicado]

15

Em C #, os métodos estáticos servem há muito tempo um propósito que nos permite chamá-los sem instanciar classes. Somente no ano posterior nos tornamos mais conscientes dos problemas do uso de métodos e classes estáticos.

  • Eles não podem usar interfaces
  • Eles não podem usar herança
  • Eles são difíceis de testar porque você não pode fazer brincadeiras e stubs

Existe uma maneira melhor? Obviamente, precisamos ser capazes de acessar os métodos da biblioteca sem classes instanciadas o tempo todo, caso contrário nosso código se tornaria bastante confuso

Uma possível solução é usar uma nova palavra-chave para um conceito antigo: o singleton . Singleton's são instâncias globais de uma classe, já que são instâncias que podemos usá-las como faríamos em classes normais. A fim de tornar seu uso agradável e prático, precisamos de um pouco de açúcar sintático no entanto

Diga que a classe Math seria do tipo singleton em vez de uma classe real. A classe real contendo todos os métodos padrão para o singleton Math é DefaultMath, que implementa a interface IMath. O singleton seria declarado como

singleton Math : IMath
{
   public Math
   {
      this = new DefaultMath();
   }
}

Se quiséssemos substituir nossa classe por todas as operações matemáticas, poderíamos criar uma nova classe

MyMath que herda DefaultMath , ou podemos herdar da interface IMath e criar uma nova classe. Para tornar nossa classe a aula de matemática ativa, você faria uma tarefa simples

Math =  new MyMath();

e voilá! da próxima vez que ligarmos para o Math.Floor, ele chamará o seu método. Note que para um singleton normal teríamos que escrever algo como Math.Instance.Floor, mas o compilador elimina a necessidade da propriedade Instance

Outra ideia seria definir os singletons como Preguiçosos para que sejam instanciados somente quando forem chamados pela primeira vez, como

lazy singleton Math : IMath

O que você acha, teria sido uma solução melhor que métodos e classes estáticos? Existe algum problema com esta abordagem?

Adendo

Alguns pontos foram levantados, que um dos principais benefícios dos métodos estáticos é ter métodos que são "sem estado" e, assim, livres de efeitos colaterais, até certo ponto, do ponto de vista de simultaneidade. Eu sinceramente concordo que esse é um ponto válido. No entanto, estamos misturando dois problemas e problemas diferentes aqui: um é tornar alguns métodos invocáveis e acessíveis globalmente sem ter que criar explicitamente uma instância. O outro é ter métodos que são sem estado.

O segundo problema poderia ser resolvido pelo método tendo algo parecido com uma palavra-chave sem estado que, da mesma forma que a estática, impedia-os de chamar isso ou talvez fizessem ainda mais para garantir que não houvesse efeitos colaterais. Com singletons ao invés de classes estáticas e algo parecido com classes e métodos sem estado, eu acho que você teria os seguintes prós e contras

Pro's

  • Métodos "estáticos" em outras classes e classes de estrutura poderia ser facilmente projetado para ser substituível
  • Classes seriam mais fáceis de testar
  • O uso de instâncias, em vez de classes estáticas, significa que os padrões de design funcionam melhor (coisas como fábricas)
  • Sem limitação de herança e polimorfismo em contraste com estática

Contras

  • Talvez um pouco pior desempenho?
  • Maus programadores farão tudo em singletons para tê-los globalmente acessíveis em vez de usar a injeção de dependência, talvez você deva ser capaz de acessar somente métodos singleton e não propriedades / campos para evitar variáveis globais:)
  • ?

Talvez o Math fosse um mau exemplo, mas imagine se os métodos de string do .Net fossem ineficientes, você poderia facilmente substituí-los pelo seu próprio usando este método. Ou alguma classe de terceiros tem um método singleton que você queria alterar um pouco, você poderia herdar e alterar esse método

    
por Homde 10.03.2011 / 14:06
fonte

3 respostas

15

Não há diferença nos perigos mudando seus métodos estáticos em métodos de instância em um singleton. Eles são efetivamente o mesmo.

Às vezes você simplesmente tem funções. Uma função em termos matemáticos, não contém estado. É simplesmente os cálculos para aplicar a uma entrada para determinar sua saída. Por exemplo, Math.Sqrt não contém nenhum estado. Ele executa um cálculo no valor fornecido e retorna uma resposta derivada desse valor. Em C, C ++, Ruby, Perl e outras linguagens que suportam funções simples, você não precisa vincular essa função a nenhuma classe, estática ou não. Em Java e C #, todas as funções precisam estar ligadas a alguma classe - o que é tecnicamente um hack.

É importante notar que as funções verdadeiras são perfeitas para permanecer um método estático. Por exemplo, tudo na classe Math está definido corretamente. Eu não gosto de fazer algo estúpido como Math.Instance.Sqrt(x) apenas para satisfazer um padrão "sem método estático".

A verdade é que, neste caso, a propriedade Instance seria o tipo de método estático que causa mais problemas. O motivo? Não é uma função - mantém o estado e altera seu comportamento com base no estado interno. No primeiro acesso, ele determina que a instância global não existe, portanto, cria. Nos acessos subseqüentes, ele reutiliza essa instância. Em ambientes multithread, isso pode criar uma condição de corrida que pode potencialmente criar várias instâncias da classe Math . Eventualmente, um vencerá e permanecerá armazenado, enquanto os outros receberão o lixo coletado. Com a classe Math , a condição de corrida seria simplesmente uma ineficiência acessível, mas se o singleton precisava manter o estado de todos os seus clientes, isso seria um problema. Se você adicionar lock s ao redor do acesso singleton, você introduziu um grande afunilamento de desempenho para aplicativos multi-thread que só piora com o número de threads. Isso ocorre principalmente porque o bloqueio só é necessário até que a instância seja criada.

Permita que suas funções sejam o mais próximo possível das funções, e você nunca precisa se preocupar com esses problemas de um único ponto. Em linguagens que suportam funções, implemente-as como funções. Em linguagens que exigem um método estático, torne-as um método estático. Singletons introduzem vários problemas que só aparecem quando você se aprofunda no seu projeto.

    
por 10.03.2011 / 15:43
fonte
5

Se a pergunta for "O que usamos para substituir classes estáticas?"

Singletons não são a resposta.

Injeção de dependência é.

Dito isso, estou usando singletons para substituir classes estáticas em uma base de código herdada, neste caso, é muito mais viável fazer essa alteração do que adicionar DI, e permite que o código antigo seja testado muito mais rápido. Veja este pergunta de estouro de pilha para saber mais sobre isso

Atualizar
P1: Como você usaria a injeção de dependência em um nível de estrutura para funções como matemática e arquivo?

No nível do framework? Já que temos permissão para adicionar suporte ao idioma para Singleton , adicionamos suporte no framework para DI em namespaces como Math, IO etc.
A configuração da máquina fornecerá seções que - por padrão - adicionam os tipos de estrutura destes, ganchos que podemos remover nos arquivos app.config e adicionar nossos próprios (ou outros para testes)

Q2: Além disso, se você pudesse definir a classe que um singleton usaria para classes diferentes, isso não seria uma injeção de dependência de fato?

Parece confuso para mim, então ao invés do seu singleton representar um objeto single , ele agora representa múltiplos objetos e serve diferentes dependendo do contexto de como é chamado? Não seria um a) um multipleton eb) seria um pesadelo para tentar acompanhar o que qualquer método Math vai fazer dependendo de como e onde você o chama.

    
por 10.03.2011 / 14:19
fonte
0

A questão da interface e herança de classes estáticas me manteve ocupado por um tempo, também, embora em um nível teórico mais do que prático.

Há também algumas perguntas sobre a SO lidar com o assunto.

Finalmente, resolvi uma solução que consiste essencialmente em

  • a separação da parte "classe real" e a parte "classe estática" em 2 classes
  • um atributo de classe que atribui uma classe à sua classe pseudo-estática
  • uma classe auxiliar (estática) recuperando e instanciando a classe pseudo-estática para uma classe

Para mais detalhes, leia esta minissérie no meu blog .

    
por 19.01.2012 / 21:11
fonte