Intenção e exemplo
Um registry imutável de dados carregados uma vez pode ter ciclo de vida único. Em C#, inicialização estática é thread-safe:
public sealed class MimeTypes
{
private static readonly Lazy<MimeTypes> _instance =
new(() => new MimeTypes());
public static MimeTypes Instance => _instance.Value;
private MimeTypes() { }
}Isso garante uma única instância no processo, não no cluster: cada servidor terá a sua. Estado compartilhado mutável introduz concorrência e torna os testes dependentes de ordem.
Por que Singleton costuma ser controverso?
Uma chamada global como Logger.Instance não revela no construtor que o componente depende de logging. Testes podem não substituir o estado, e componentes passam a conhecer um serviço concreto. A aplicação também pode confundir “uma instância por processo” com “uma instância por requisição” ou “um recurso distribuído único”.
Em aplicações com container de DI, registre o serviço com ciclo de vida singleton e injete a interface nos consumidores. Isso deixa o ciclo de vida único, mas mantém a dependência explícita e substituível.
Quando usar e alternativas
Use com parcimônia para objetos sem estado mutável cuja unicidade local seja requisito claro. Evite-o como atalho para compartilhar dados ou acessar serviços. Considere DI, um módulo imutável ou uma fábrica explícita. Para criação configurável, veja Factory Method e Abstract Factory; consulte também Clean Architecture.
Mais padrões
Leia o guia dos 23 Design Patterns GoF e compare com Prototype, que cria instâncias por cópia em vez de impor unicidade.