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.