Files
freemo d9e5668cec fix(skills): comprehensive final audit pass for programming-patterns skill
Fixes and improvements from exhaustive audit:

Consistency fixes in SKILL.md:
- 'Pipe & Filter' → 'Pipe and Filter' (one stray '&' found and corrected)
- 'Singleton for factory instance' → clarified to 'register factory as
  singleton-scoped via DI container' (less misleading wording)
- Documentation Format section updated with note that SKILL.md itself is the
  authoritative source for related-pattern combinations

Coverage fix — Related Patterns sections:
- Added '## Related Patterns' to ALL 94 pattern files (was 0/94)
- Each section lists 3–6 related patterns with relationship descriptions
- Covers: why they're related, when to prefer one vs the other,
  and which are often confused

SOLID principles → Creational → Structural → Behavioral → Architectural →
Concurrency → Functional → Resilience → Data Access → Messaging →
Testing → Error Handling → Microservice — all 13 categories covered

Code verification:
- Python: 0 failures (all 85 testable blocks pass)
- Go: 0 failures (all 76 testable blocks pass)
- JavaScript: 0 failures (all 78 testable blocks pass)
- All 239 code blocks verified correct after edits

Final skill state:
- 108 files, 36,524 lines across 13 reference categories
- 94/94 pattern files have Related Patterns sections
- 2,815-line SKILL.md with 67 decision trees, 23 scenarios,
  0 broken references, 0 naming inconsistencies
2026-04-15 13:22:13 -04:00

8.2 KiB

Singleton

Problem

You need exactly one instance of a class shared across the entire application — a config manager, logging service, or connection pool. Multiple instances would cause inconsistencies, resource waste, or data conflicts.

Solution

Ensure a class has only one instance and provide a global point of access to it. The class itself controls instantiation and returns the same instance on every request.

         ┌───────────────────┐
         │ ConfigManager      │
         │ -_instance: static │
         │ +getInstance()     │
         │ +get(key)          │
         │ +set(key, value)   │
         └───────────────────┘
              │
              │ always returns
              ▼ same instance
         ┌────────────────┐
         │ { db_host: ... }│
         │ { port: 8080 } │
         └────────────────┘

When to Use

  • There must be exactly one instance (hardware interface, thread pool, config store).
  • The instance must be globally accessible.
  • Lazy initialization is desired — create only when first needed.

When to Avoid

In most cases, prefer Dependency Injection over Singleton. Singletons are problematic because:

  • They introduce hidden global state that makes code hard to reason about.
  • They make unit testing extremely difficult — you can't substitute mocks easily.
  • They create tight coupling — every consumer depends on the concrete singleton class.
  • They violate the Single Responsibility Principle — the class manages its own lifecycle.
  • Concurrency bugs hide in lazy initialization without careful synchronization.

Rule of thumb: If you're reaching for Singleton, first ask "Can I inject this instead?" See Dependency Injection for the preferred approach.

Pseudocode

class ConfigManager:
    static instance: ConfigManager = null
    field data: Map<string, string>

    private constructor():
        data = loadFromFile("config.json")

    static method getInstance() -> ConfigManager:
        if instance == null:
            instance = new ConfigManager()
        return instance

    method get(key) -> string:
        return data[key]

    method set(key, value):
        data[key] = value

// Usage
config = ConfigManager.getInstance()
config.set("db_host", "localhost")
// Anywhere else in the app:
same = ConfigManager.getInstance()  // same object
print(same.get("db_host"))  // "localhost"

Python

"""Singleton — ConfigManager using __new__."""


class ConfigManager:
    """Thread-safe singleton using __new__.

    NOTE: For production code, prefer dependency injection.
    This demonstrates the *pattern*, not a recommendation.
    """

    _instance: "ConfigManager | None" = None

    def __new__(cls) -> "ConfigManager":
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._data: dict[str, str] = {}
            cls._instance._initialized = False
        return cls._instance

    def __init__(self) -> None:
        if not self._initialized:
            # Simulate loading config
            self._data = {"db_host": "localhost", "port": "5432"}
            self._initialized = True
            print("ConfigManager initialized (this happens only once)")

    def get(self, key: str, default: str = "") -> str:
        return self._data.get(key, default)

    def set(self, key: str, value: str) -> None:
        self._data[key] = value


if __name__ == "__main__":
    # First access — initializes
    config1 = ConfigManager()
    config1.set("db_host", "prod-db.example.com")
    print(f"config1.get('db_host') = {config1.get('db_host')}")

    # Second access — same instance
    config2 = ConfigManager()
    print(f"config2.get('db_host') = {config2.get('db_host')}")
    print(f"config1 is config2: {config1 is config2}")

    # ---- Why DI is better ----
    print("\n--- Why Dependency Injection is preferred ---")
    print("Problem: How do you test code that uses ConfigManager()?")
    print("  - You can't substitute a mock without monkey-patching.")
    print("  - Global state leaks between tests.")
    print("Solution: Pass config as a constructor argument instead.")
    print("  See dependency-injection.md for the full pattern.")

    print("\n--- Singleton demo complete ---")

Go

// singleton.go — ConfigManager using sync.Once
package main

import (
	"fmt"
	"sync"
)

// ConfigManager holds application configuration.
type ConfigManager struct {
	data map[string]string
}

var (
	configInstance *ConfigManager
	configOnce     sync.Once
)

// GetConfigManager returns the singleton instance.
// sync.Once guarantees thread-safe, one-time initialization.
func GetConfigManager() *ConfigManager {
	configOnce.Do(func() {
		fmt.Println("ConfigManager initialized (this happens only once)")
		configInstance = &ConfigManager{
			data: map[string]string{
				"db_host": "localhost",
				"port":    "5432",
			},
		}
	})
	return configInstance
}

func (c *ConfigManager) Get(key string) string {
	return c.data[key]
}

func (c *ConfigManager) Set(key, value string) {
	c.data[key] = value
}

func main() {
	// First access — initializes
	config1 := GetConfigManager()
	config1.Set("db_host", "prod-db.example.com")
	fmt.Printf("config1.Get(\"db_host\") = %s\n", config1.Get("db_host"))

	// Second access — same instance
	config2 := GetConfigManager()
	fmt.Printf("config2.Get(\"db_host\") = %s\n", config2.Get("db_host"))
	fmt.Printf("config1 == config2: %v\n", config1 == config2)

	// ---- Why DI is better ----
	fmt.Println("\n--- Why Dependency Injection is preferred ---")
	fmt.Println("Problem: How do you test code that calls GetConfigManager()?")
	fmt.Println("  - You can't substitute a mock without build tags or init tricks.")
	fmt.Println("  - sync.Once means you can't reset between tests.")
	fmt.Println("Solution: Accept a Config interface as a function/struct parameter.")
	fmt.Println("  See dependency-injection.md for the full pattern.")

	fmt.Println("\n--- Singleton demo complete ---")
}

JavaScript

// singleton.js — ConfigManager using module-scoped instance
//
// In JavaScript/Node.js, modules are cached after first evaluation,
// so exporting an instance IS effectively a singleton.

class ConfigManager {
  constructor() {
    // Guard against direct construction
    if (ConfigManager._instance) {
      return ConfigManager._instance;
    }
    this._data = { db_host: "localhost", port: "5432" };
    ConfigManager._instance = this;
    console.log("ConfigManager initialized (this happens only once)");
  }

  get(key) {
    return this._data[key] ?? "";
  }

  set(key, value) {
    this._data[key] = value;
  }
}

// --- Demo ---

// First access — initializes
const config1 = new ConfigManager();
config1.set("db_host", "prod-db.example.com");
console.log(`config1.get("db_host") = ${config1.get("db_host")}`);

// Second access — same instance
const config2 = new ConfigManager();
console.log(`config2.get("db_host") = ${config2.get("db_host")}`);
console.log(`config1 === config2: ${config1 === config2}`);

// ---- Why DI is better ----
console.log("\n--- Why Dependency Injection is preferred ---");
console.log("Problem: How do you test code that uses new ConfigManager()?");
console.log("  - You can't substitute a mock without module mocking hacks.");
console.log("  - Singleton state leaks between test files.");
console.log("Solution: Pass config as a constructor/function argument.");
console.log("  See dependency-injection.md for the full pattern.");

console.log("\n--- Singleton demo complete ---");
  • Dependency Injection — prefer DI over singleton; DI achieves the same shared-instance goal without hidden global state or test difficulty.
  • Flyweight — flyweight shares many instances of fine-grained objects; singleton ensures exactly one instance of a coarse-grained object.
  • Facade — a facade is often implemented as a singleton providing a single entry point to a subsystem.
  • Monostate — monostate (all instances share the same state) is an alternative to singleton that doesn't restrict instantiation.