OOP Concepts · Design Patterns

Singleton Pattern

The Singleton pattern makes sure a class has exactly one instance and gives everyone the same one — useful for app settings, caches and connection pools.

Intermediate 5 min Lesson 29 of 38 Runnable examplesExamples use modern JavaScript (ES2022 private fields) and C# 13.
0/38 in OOP Concepts
On this page · 5 min · Intermediate

In simple words#

A country has one Prime Minister at a time. Singleton ensures a class has exactly one shared instance.

Singleton
Only one instance exists.
Global access
Everyone uses the same instance.
In ASP.NET Core
Prefer AddSingleton in DI over hand-written singletons.

Learning objectives#

  • Implement a Singleton in JavaScript and C#
  • Understand why the constructor must be protected
  • Use module and DI singletons instead of hand-made ones
  • Know the testing and threading pitfalls

Explanation#

A Singleton hides its constructor and exposes a static access point (AppConfig.instance). The first call creates the object; later calls return the same one.

In C#, the simplest thread-safe version uses Lazy<T>, which creates the instance once even if many threads ask at the same moment.

In JavaScript, a module is already a singleton: every import of a module gets the same exported object. That is the most common JavaScript “singleton”.

In ASP.NET Core, prefer builder.Services.AddSingleton<ICache, MemoryCache>(): the container guarantees one instance, and tests can still replace it.

Singletons are global state in disguise. They make tests depend on each other and hide dependencies. Use them for truly shared, mostly read-only things.

Syntax#

C# Illustrative
public sealed class AppConfig
{
    private static readonly Lazy<AppConfig> _instance = new(() => new AppConfig());
    public static AppConfig Instance => _instance.Value;


    private AppConfig() { }          // nobody else can call new


    public string Theme { get; set; } = "light";
}


// Preferred in ASP.NET Core:
builder.Services.AddSingleton<IAppConfig, AppConfig>();

Code example: One configuration for the whole app#

JavaScriptOne configuration for the whole app Runnable
class AppConfig {
  static #instance = null;
  #settings = new Map();


  constructor() {
    if (AppConfig.#instance) throw new Error('Use AppConfig.instance');
  }


  static get instance() {
    if (!AppConfig.#instance) AppConfig.#instance = new AppConfig();
    return AppConfig.#instance;
  }


  set(key, value) { this.#settings.set(key, value); }
  get(key) { return this.#settings.get(key); }
}


const a = AppConfig.instance;
a.set('theme', 'dark');


const b = AppConfig.instance;        // somewhere else in the app
console.log(a === b);
console.log(b.get('theme'));


try { new AppConfig(); } catch (e) { console.log(e.message); }


// The everyday JavaScript singleton: a module-level object
const idGenerator = (() => {
  let last = 0;
  return { next: () => ++last };
})();
console.log(idGenerator.next(), idGenerator.next());
Expected output
true
dark
Use AppConfig.instance
1 2
Opens this example in the full playground

Step-by-step explanation#

  1. static #instance holds the single object; it starts as null.
  2. The instance getter creates the object on first use (lazy creation) and returns the same one afterwards.
  3. a and b are the same object, so a setting saved through a is visible through b.
  4. Calling new directly is blocked once the instance exists.
  5. idGenerator shows the simpler module-style singleton: one closure, shared state.

Real-world use case#

An ASP.NET Core app registers IMemoryCache, HttpClient handlers and configuration as singletons. One cache serves every request, so data loaded once is reused across users.

Common mistakes#

Mutable singleton state in web apps

A singleton holding the “current user” leaks data between requests and users. Request data belongs in scoped services.

Not thread-safe creation (C#)

if (_instance == null) _instance = new() can create two instances under load. Use Lazy<T> or the DI container.

Singletons that hide dependencies

Calling AppConfig.Instance deep inside a method makes the class impossible to test with different settings. Inject the config instead.

Best practices#

  • Prefer DI singletons over hand-written ones
  • Keep singleton state read-only or thread-safe
  • Use Lazy<T> for hand-written C# singletons
  • Never store per-user data in a singleton

Remember this#

Singleton = one instance, one access point.

In JS, a module is already a singleton.

In ASP.NET Core: AddSingleton, not static Instance.

Practice exercise#

Your task: Create a Logger singleton with a static getInstance() method and a log(msg) method that stores messages in an array. Log from two “different places” and show both messages are in the same logger.

Need a hint?
Both places call Logger.getInstance(); check getInstance() === getInstance().
Show solution
JavaScript Runnable
class Logger {
  static #instance;
  messages = [];
  static getInstance() { return (Logger.#instance ??= new Logger()); }
  log(msg) { this.messages.push(msg); }
}
Logger.getInstance().log('from checkout');
Logger.getInstance().log('from login');
console.log(Logger.getInstance().messages);                   // [ 'from checkout', 'from login' ]
console.log(Logger.getInstance() === Logger.getInstance());   // true
Opens this example in the full playground

Mini quiz#

Singleton Pattern quizMultiple choice Question 1 of 3

What does the Singleton pattern guarantee?

Score so far: 0/0

Want more? Practise all OOP Concepts MCQs · OOP Concepts interview questions

My notes#

Saved in this browser.

Progress is saved in this browser. Sign in to sync.