design_patterns_creational 38 Q&As

Design Patterns Creational FAQ & Answers

38 expert Design Patterns Creational answers researched from official documentation. Every answer cites authoritative sources you can verify.

unknown

38 questions
A

Factory Method defines interface for creating objects but lets subclasses decide which class to instantiate. Defers object creation to subclasses. Use when: (1) Class can't anticipate type of objects to create, (2) Want subclasses to specify objects they create, (3) Need flexibility in object creation. Pattern: abstract class Creator {abstract factoryMethod(): Product; operation() {const product = this.factoryMethod();}}. Subclasses: class ConcreteCreatorA extends Creator {factoryMethod() {return new ProductA;}}. Example: Document editor with DocumentFactory. PDFFactory creates PDFDocument, WordFactory creates WordDocument. Client code: const factory = new PDFFactory(); const doc = factory.createDocument(). Benefits: Loose coupling (client doesn't know concrete classes), extensibility (add new product types), follows Open/Closed Principle. Different from Abstract Factory (creates families, not single products).

99% confidence
A

Abstract Factory provides interface for creating families of related objects without specifying concrete classes. Factory Method creates one product, Abstract Factory creates product families. Pattern: interface GUIFactory {createButton(): Button; createCheckbox(): Checkbox}. Implementations: WindowsFactory, MacFactory create Windows/Mac UI components. Use when: Need multiple related objects (button + checkbox + dialog), want consistent product families. Example: const factory = new WindowsFactory(); const button = factory.createButton(); const checkbox = factory.createCheckbox() - guaranteed compatible Windows components. Difference: Factory Method uses inheritance (subclass overrides method), Abstract Factory uses composition (interface with multiple creation methods). Factory Method: one product hierarchy. Abstract Factory: multiple related product hierarchies. Use Abstract Factory for cross-platform UI, themed components, database + ORM + connection matched sets.

99% confidence
A

Use Factory when object creation logic is complex, varies, or should be centralized. Direct instantiation: new User() - simple, fine for straightforward objects. Factory: when (1) Creation requires logic (config, validation, dependencies), (2) Type determined at runtime (user input, config), (3) Want to encapsulate creation details, (4) Need to swap implementations (testing, feature flags). Example needing Factory: const user = UserFactory.create(userData) - validates data, sets defaults, assigns ID, logs event. Direct new User(userData) spreads this logic everywhere. Factory benefits: DRY (one creation point), testability (mock factory), flexibility (change creation without touching clients). Don't use Factory for: Simple POJOs, DTOs, configuration objects (overkill). Use for: Domain entities with business rules, objects requiring dependencies, polymorphic object creation. Rule: If new ClassName() needs >3 lines setup code, consider Factory.

99% confidence
A

Telescoping constructor antipattern: Multiple constructor overloads with increasing parameters, leading to exponential constructor combinations. Problem: class Pizza {constructor(size, cheese?, pepperoni?, mushrooms?, onions?, olives?)} - 6 parameters = messy calls with nulls: new Pizza('large', true, false, null, true, false). Hard to read, error-prone (parameter order confusion), ugly with many nulls. Threshold: >4-5 parameters = telescoping problem. Builder solution: Breaks construction into steps. Pattern: new PizzaBuilder().size('large').cheese().onions().olives().build() - fluent, readable, no null parameters. Benefits: (1) Self-documenting code (method names explain parameters), (2) No parameter order confusion, (3) Optional parameters without overloads, (4) Can validate during construction. Example: HTTPRequestBuilder().url('...').method('POST').headers({...}).body({...}).build() vs new HTTPRequest(url, 'POST', headers, body, null, null, null, timeout). When parameters exceed 4-5, Builder is cleaner. Modern alternative: JavaScript object literals with destructuring: new Pizza({size: 'large', cheese: true, onions: true, olives: true}) - simpler for simple cases.

99% confidence
A

Builder separates object construction from representation, allowing step-by-step construction with fluent interface. Use for: (1) Objects with many optional parameters (avoids telescoping constructors), (2) Multi-step construction process, (3) Need different representations of same construction. Pattern: class UserBuilder {private name; private email; private age; setName(name) {this.name = name; return this;} setEmail(email) {this.email = email; return this;} build() {return new User(this.name, this.email, this.age);}}. Usage: const user = new UserBuilder().setName('John').setEmail('[email protected]').setAge(30).build(). Benefits: Readable construction, optional parameters without constructor overload, immutable objects (set all at once in build()). Alternative: JavaScript object literals. Use Builder for: Complex configuration, validation during building, creating immutable objects. Don't use for simple objects (2-3 required fields).

99% confidence
A

Fluent API uses method chaining where each method returns this for readable, chainable calls. Pattern: class QueryBuilder {private query = ''; select(fields: string[]) {this.query += SELECT ${fields.join(', ')}; return this;} from(table: string) {this.query += FROM ${table}; return this;} where(condition: string) {this.query += WHERE ${condition}; return this;} build(): string {return this.query;}}. Usage: const sql = new QueryBuilder().select(['id', 'name']).from('users').where('age > 18').build(). TypeScript type safety: Use interfaces to enforce method order. Pattern: interface BuilderWithSelect {from(t: string): BuilderWithFrom}; interface BuilderWithFrom {where(c: string): BuilderWithWhere; build(): string}. Forces select() → from() → build() order, prevents invalid states (can't call where() before from()). Immutability option: Return new instance instead of this: select(fields) {const b = new QueryBuilder(); b.query = this.query + ...; return b;} - pure functions, no side effects. Modern frameworks: React Query, Prisma use fluent APIs. Benefits: Readable DSL, type-safe construction, self-documenting code.

99% confidence
A

Prototype creates new objects by cloning existing instance rather than instantiating from class. Use when: (1) Creating new object is expensive (database query, file I/O, complex calculation), (2) Objects differ only slightly (clone and modify), (3) Want to avoid subclass explosion for configurations. Pattern: interface Prototype {clone(): Prototype}. class ConcretePrototype implements Prototype {clone() {return Object.assign({}, this);}}. Usage: const original = new ConcretePrototype(); const copy = original.clone(). JavaScript native: Object.assign(), spread operator {...obj}, Array.from(). Deep vs shallow clone: Shallow copies references, deep copies nested objects. Use JSON.parse(JSON.stringify(obj)) for deep (loses functions), or lodash.cloneDeep(). Example: Configuration objects with base template, Game entities with similar stats, UI components with default props. Benefits: Avoids expensive initialization, dynamic configuration without classes. Don't use when: Simple constructors are fast, deep cloning is complex.

99% confidence
A

structuredClone() is native JavaScript API (2021-2022, now standard) for deep cloning, replacing JSON.parse(JSON.stringify()). Pattern: const clone = structuredClone(original) - creates deep copy with all nested objects, handles circular references, preserves types. Benefits over JSON method: (1) Preserves Date, RegExp, Map, Set, ArrayBuffer objects (JSON converts to strings/nulls), (2) Handles circular references (JSON throws error), (3) Copies getters/setters (JSON loses them), (4) Faster for large objects. Limitations: (1) Loses prototype chain (class instances become plain objects), (2) Cannot clone functions, DOM nodes, symbols. Use: const user = structuredClone(templateUser); user.address.city = 'NYC' - doesn't affect template. Shallow clone comparison: {...obj} or Object.assign({}, obj) only copy top level, nested objects are references. Use shallow when: Simple primitives-only objects. Use structuredClone when: Nested objects, need true independence, working with Date/Map/Set. Browser support: Node 17+, all modern browsers (2022+). Fallback: lodash.cloneDeep for older environments.

99% confidence
A

Singleton ensures class has only one instance with global access point. Implementation: private constructor, static getInstance(). Pattern: class Singleton {private static instance; private constructor() {} static getInstance() {if (!this.instance) this.instance = new Singleton(); return this.instance;}}. Problems: (1) Global state (hard to track who modifies), (2) Tight coupling (everything depends on singleton), (3) Difficult testing (shared state between tests), (4) Violates Single Responsibility (manages instance + business logic), (5) Hides dependencies (unclear what class needs). Modern alternatives: Dependency injection with singleton scope (framework manages), ES6 modules (naturally singletons), const instances. Use Singleton for: Logger, configuration, connection pool (genuinely need one instance). Avoid for: Business logic, services (use DI instead). TypeScript: Module-level const Config = new ConfigClass() simpler than Singleton pattern.

99% confidence
A

ES6 modules are singletons because JavaScript engines cache them after first execution. Pattern: export const config = new ConfigService() - first import executes module and creates instance, all subsequent imports receive the cached instance. Node.js (require/import) and browsers guarantee same module = same instance. Module evaluated once per application lifecycle. Example: logger.ts exports const logger = new Logger(); importing from multiple files returns identical logger instance. Mechanism: V8 engine maintains module cache keyed by resolved file path. import {db} from './database' in 10 files = one Database instance. Differs from Singleton pattern: no getInstance() method needed, module system handles caching automatically. Works in Node.js 22+, all modern browsers (2024+). Module exports are framework-agnostic singletons built into JavaScript specification.

99% confidence
A

Use ES6 modules for configuration objects, loggers, database connections, and shared state managers. Benefits over Singleton pattern: (1) No getInstance() boilerplate, (2) Tree-shakeable (unused exports removed by bundlers), (3) Module scope instead of global, (4) Testable with jest.mock('./module'), (5) Immutable exports via const. Pattern: export const config = new Config(); vs class Config {static getInstance()}. Use modules when: Simple singleton needs, eager initialization acceptable, no complex lifecycle. Don't use modules when: Need lazy initialization (modules execute on import), must reset state between tests (hard to clear module cache), require lifecycle hooks (startup/shutdown). Testing: jest.mock('./logger', () => ({logger: mockLogger})) replaces module. Modern best practice (2025): ES6 modules for simple cases, DI containers (NestJS @Injectable({scope: 'singleton'}), InversifyJS) for complex apps needing lifecycle control and dependency management.

99% confidence
A

JavaScript is single-threaded via event loop, but async operations cause race conditions. Use Singleton Promise pattern: cache the promise itself, not just the result. Pattern: class AsyncSingleton {private static instance: AsyncSingleton; private static initPromise: Promise | null; private constructor() {} static getInstance(): Promise {if (!this.initPromise) {this.initPromise = this.initialize();} return this.initPromise;} private static async initialize(): Promise {const instance = new AsyncSingleton(); await instance.setup(); this.instance = instance; return instance;}}. Critical: Check initPromise existence, not completion - prevents duplicate initialization when multiple callers request instance before first completes. Worker threads: Each worker has separate module scope. Use SharedArrayBuffer or message passing for cross-worker sharing. Modern best practice (2025): Avoid Singleton pattern entirely, use DI containers (NestJS @Injectable({scope: 'singleton'}), InversifyJS) providing lifecycle management without global state problems. Alternative: Top-level await in ES modules: const db = await Database.connect(); export default db - naturally singleton with clean async initialization.

99% confidence
A

Dependency Injection solves Singleton problems while providing controlled single instances. Singleton problems: Global state, hidden dependencies, tight coupling, hard testing. DI solution: Framework-managed singleton scope provides one instance while keeping explicit dependencies. Pattern: @Injectable({scope: 'singleton'}) class ConfigService. Usage: constructor(private config: ConfigService) - dependency explicit in signature, testable with mocks. DI advantages over Singleton: (1) Testability - inject mock: TestBed.overrideProvider(ConfigService, {useValue: mockConfig}). (2) Explicit dependencies - constructor reveals what class needs. (3) No global state - container manages lifecycle. (4) Swap implementations - bind different class without changing code. (5) Follows SOLID - depend on abstractions, single responsibility. When DI is better (most cases): Business logic, services, repositories, any class needing testability. When Singleton acceptable: True infrastructure shared across app (logger, metrics collector) AND don't need testing. Modern frameworks (NestJS, Angular, Spring) use DI with singleton scope as default. Singleton pattern considered legacy - use DI for managed singletons instead. DI = better Singleton with dependency visibility and testability.

99% confidence
A

Object Pool manages reusable object pool to avoid expensive creation/destruction. Pattern: class ObjectPool {private available: T[] = []; private inUse: Set = new Set(); acquire(): T {let obj = this.available.pop(); if (!obj) obj = this.create(); this.inUse.add(obj); return obj;} release(obj: T) {this.inUse.delete(obj); this.available.push(obj);}}. Use when: (1) Object creation is expensive (database connections, threads), (2) Objects are frequently created and destroyed, (3) Number of objects can be limited. Examples: Database connection pools (pg.Pool, mysql2 pool), HTTP agent connection pooling (Node http.Agent), Worker thread pools. Pattern: const pool = new Pool({max: 10}); const conn = await pool.acquire(); try {await conn.query(sql);} finally {pool.release(conn);}. Benefits: Performance (reuse expensive objects), resource limits (max 10 connections). Don't use for: Cheap objects (plain objects, strings), objects with complex state requiring reset. Node.js built-in: http.Agent pools sockets, cluster module pools processes.

99% confidence
A

Use DI when: (1) Dependencies are known at application startup, (2) Want framework-managed lifecycle, (3) Need application-wide singleton/scoped instances. Use Factory when: (1) Object type determined at runtime (user input, data), (2) Creating transient objects during execution, (3) Complex creation logic. DI: Dependencies declared upfront, injected by container. Pattern: @Injectable() class UserService {constructor(private db: Database, private logger: Logger)}. Framework (NestJS, Angular) resolves dependencies. Factory: Runtime object creation. Pattern: const user = UserFactory.create(type, data). Combination: Inject factories via DI: class OrderService {constructor(private paymentFactory: PaymentFactory)} - factory injected but creates payment processors dynamically. DI advantages: Testability (mock entire container), configuration (env-based bindings), lifecycle management. Factory advantages: Flexibility (runtime decisions), encapsulated creation logic. Use DI for infrastructure dependencies (DB, cache, logger), Factory for domain entities/strategies created during request processing.

99% confidence
A

React factory creates components dynamically based on type/props. Pattern 1: Function factory: const ComponentFactory = {button: ButtonComponent, input: InputComponent, select: SelectComponent}; function FormField({type, ...props}) {const Component = ComponentFactory[type]; return <Component {...props} />;}. Usage: . Pattern 2: React.createElement (built-in factory): React.createElement('div', {className: 'container'}, children) - JSX compiles to this. Custom: function createComponent(type, props) {return React.createElement(ComponentFactory[type], props);}. Use cases: (1) Form builders (render field based on schema), (2) Dynamic dashboards (widget based on config), (3) CMS (component based on content type). Example: const schema = [{type: 'input', name: 'email'}, {type: 'button', label: 'Submit'}]; schema.map(field => <FormField {...field} key={field.name} />). Benefits: DRY (one render logic), extensible (add types to factory), data-driven UI. TypeScript safety: type ComponentType = keyof typeof ComponentFactory; function FormField({type}: {type: ComponentType}). Modern alternative: Compound components, render props. Use factory for: Type-based rendering, plugin systems, theme variations.

99% confidence
A

NestJS factory providers create instances dynamically based on dependencies or configuration. Pattern: useFactory with inject. Example: @Module({providers: [{provide: 'DATABASE_CONNECTION', useFactory: async (config: ConfigService) => {return await createConnection(config.get('db'));}, inject: [ConfigService]}]}) - factory receives ConfigService, creates connection. Usage: constructor(@Inject('DATABASE_CONNECTION') private db: Connection). Use cases: (1) Async initialization (database, external service), (2) Conditional creation based on environment, (3) Complex object requiring multiple dependencies. Advanced: Factory class: @Injectable() class PaymentFactory {constructor(private config: ConfigService) {} create(type: string): PaymentProcessor {if (type === 'stripe') return new StripeProcessor(this.config.get('stripe')); if (type === 'paypal') return new PayPalProcessor(this.config.get('paypal'));}}. Register: providers: [PaymentFactory]. Inject: constructor(private factory: PaymentFactory). Benefits: Leverage DI for factory dependencies, framework-managed lifecycle, testable (override factory in tests). Modern NestJS (2025): Use scope: 'transient' for non-singleton instances, combine with useFactory for flexible instance creation. Pattern: Factory creates objects, DI injects factory.

99% confidence
A

Simple Factory: Static method creates objects based on parameter, not a true design pattern (utility). Pattern: class ShapeFactory {static create(type: string) {if (type === 'circle') return new Circle(); if (type === 'square') return new Square();}}. Usage: const shape = ShapeFactory.create('circle'). Factory Method: Template method defining interface, subclasses implement creation. Pattern: abstract class ShapeCreator {abstract createShape(): Shape}. class CircleCreator extends ShapeCreator {createShape() {return new Circle();}}. Differences: Simple Factory is one class with static method, Factory Method is inheritance-based. Simple Factory uses switch/if, Factory Method uses polymorphism. Simple Factory violates Open/Closed (modify to add types), Factory Method extends without modification. Use Simple Factory for: Straightforward, rarely-changing object creation. Use Factory Method for: Extensible creation, plugin architecture, framework hooks. Simple Factory simpler but less flexible. Factory Method more complex but extensible.

99% confidence
A

Registry (Service Locator) maintains central repository of singleton instances accessed by key. Pattern: class Registry {private static instances = new Map<string, any>(); static register(key: string, instance: T): void {this.instances.set(key, instance);} static resolve(key: string): T {if (!this.instances.has(key)) throw new Error(Service '${key}' not registered); return this.instances.get(key)!;}}. Usage: Registry.register('db', new Database()); const db = Registry.resolve('db'). Type-safe version: Use Symbol keys instead of strings to prevent collisions: const DB_TOKEN = Symbol('Database'); Registry.register(DB_TOKEN, db). Benefits: Single access point for multiple singletons, runtime service discovery, plugin registration (plugins register themselves on load). Problems: Hidden dependencies (unclear what service needs), runtime errors if service not registered, pull-based (code requests services vs DI push-based injection). Modern alternative (2025): DI containers with named bindings. NestJS: @Inject('DATABASE_CONNECTION') db: Database - explicit dependencies, compile-time checks. Angular Injector: injector.get('DatabaseService') - framework-managed registry. Use Registry for: Legacy systems, plugin architectures, dynamic service discovery at runtime. Avoid for new projects - use DI with explicit dependencies instead.

99% confidence
A

Lazy Initialization delays object creation until first use. Improves startup time and memory by avoiding unnecessary initialization. Pattern: class LazyService {private _instance: Expensive; get instance() {if (!this._instance) this._instance = new Expensive(); return this._instance;}}. Usage: const service = new LazyService(); service.instance - creates on first access. Benefits: (1) Faster startup (don't initialize unused services), (2) Lower memory (only used objects), (3) Handles circular dependencies. Example: class Config {private _db: Database; get db() {if (!this._db) this._db = new Database(this.dbConfig); return this._db;}} - database only connects if accessed. Trade-offs: First access slower (initialization penalty), potential race conditions (async initialization). Use for: Expensive resources (DB, caches), optional dependencies, plugins. Don't use for: Required dependencies (confusing errors), frequently-used objects (no benefit). Modern alternative: import() for code splitting (lazy module loading).

99% confidence
A

Constructor: Simple objects with 2-4 required parameters. Pattern: new User(id, name, email). Use when: Straightforward creation, all parameters required, no validation logic. Factory: Runtime type selection or complex creation logic. Pattern: UserFactory.create(type, data). Use when: Type determined at runtime, need polymorphism, encapsulate creation. Builder: Many optional parameters or step-by-step construction. Pattern: new UserBuilder().setName('John').setEmail('[email protected]').build(). Use when: >5 parameters (especially optional), want fluent API, complex validation. Examples: Constructor: new Date(year, month, day). Factory: DocumentFactory.create('PDF') returns PDFDocument. Builder: new HttpRequestBuilder().url('...').headers({...}).body({...}).build(). Combination: Factory returns Builder: UserFactory.builderForType('admin').setPermissions(...).build(). Decision: Start with Constructor. Switch to Factory if need polymorphism. Switch to Builder if parameter list grows unwieldy or need step-by-step construction.

99% confidence
A

Multiton ensures exactly one instance per unique key, while Singleton ensures one instance globally. Multiton = registry of keyed singletons. Pattern: class DatabaseConnection {private static instances = new Map<string, DatabaseConnection>(); private constructor(private dbName: string) {} static getInstance(dbName: string): DatabaseConnection {if (!this.instances.has(dbName)) {this.instances.set(dbName, new DatabaseConnection(dbName));} return this.instances.get(dbName)!;}}. Usage: const usersDB = DatabaseConnection.getInstance('users'); const ordersDB = DatabaseConnection.getInstance('orders'); const users2 = DatabaseConnection.getInstance('users'); console.log(usersDB === users2); // true - same instance for same key. Key difference: Singleton = 1 instance total, Multiton = 1 instance per unique identifier. Use cases: Database connection pools per schema, HTTP clients per API endpoint, loggers per module, caches per domain/tenant. Benefits: Controlled allocation (prevents duplicate instances for same key), resource management (limit instances), namespaced global access. Drawbacks: Same as Singleton - global state, hidden dependencies, testing difficulty, potential memory leaks (instances never released). Modern alternative (2025): DI with factory provider: @Inject('DB_FACTORY') dbFactory: (name: string) => Database - framework manages instance lifecycle, explicit dependencies, better testability. Multiton useful for resource pooling but prefer DI for business logic.

99% confidence
A

In functional programming, factories are functions that return functions or objects. Pattern 1: Function factory: const createGreeter = (greeting) => (name) => ${greeting}, ${name}!. Usage: const sayHello = createGreeter('Hello'); sayHello('John') returns 'Hello, John!'. Pattern 2: Object factory: const createUser = (name, role) => ({name, role, permissions: getPermissions(role)}). Pattern 3: Closure factory: const createCounter = () => {let count = 0; return {increment: () => ++count, get: () => count}}. Benefits: Partial application, encapsulation without classes, composable. Advanced: Currying for progressive configuration: const createFetcher = (baseURL) => (endpoint) => (options) => fetch(${baseURL}${endpoint}, options). Usage: const api = createFetcher('https://api.com'); const getUser = api('/users'); getUser({method: 'GET'}). TypeScript: Type-safe factories: type UserFactory = (name: string) => User; const createUser: UserFactory = (name) => ({name, id: generateId()}). Use for: Configuration builders, middleware pipelines, plugin systems. Modern frameworks: React hooks are factories (useState, useEffect return state/functions), Redux action creators are factories. FP factories simpler than OOP patterns - just functions returning values/functions.

99% confidence
A

Pitfall 1: Applying OOP patterns from other languages unnecessarily. TypeScript has simpler alternatives (destructuring, modules, spread operators) that solve problems without pattern complexity. Fix: Use language features first. Pitfall 2: Builder with optional/required confusion. Problem: TypeScript can't enforce which properties are required - runtime errors only. Fix: Use method chaining with typed interfaces forcing required methods called before build(). Pitfall 3: Singleton considered harmful. Problem: Global state, testing difficulty. Fix: Use ES6 modules (natural singletons) or DI with singleton scope. Pitfall 4: Factory with switch/if violates Open/Closed. Problem: if (type === 'A') return new A() - modify for new types. Fix: Use registry pattern: factory.register('A', () => new A()). Pitfall 5: Prototype shallow clone mutations. Problem: const copy = {...original} - nested objects share references. Fix: Use structuredClone(original) for deep copies (2025 standard). Pitfall 6: Async Singleton race conditions. Problem: Multiple initializations during Promise resolution. Fix: Cache promise itself: if (!this.initPromise) this.initPromise = initialize(). Pitfall 7: Over-engineering simple objects. Problem: 3-line constructor becomes 50-line Builder. Fix: Start simple, add patterns only when complexity justifies them. TypeScript's type system catches many errors at compile-time - leverage it instead of runtime pattern complexity.

99% confidence
A

ES6 modules are singletons by default - executed once, cached by JavaScript engine. Pattern: export const config = new ConfigService(); export const logger = new Logger() - first import executes module, subsequent imports get cached instance. Benefits over Singleton pattern: No getInstance() boilerplate, tree-shakeable (unused exports removed), module scope (not global), testable with mocking frameworks, immutable exports (const). Example: config.ts: export const config = new Config(); app.ts: import {config} from './config' - same config everywhere. Node.js/V8 caching: require() and import cache modules automatically. Multiple imports = same instance. Testing: jest.mock('./config') replaces entire module. Modern best practice: Use ES6 modules for simple singletons. Use DI containers for complex apps needing lifecycle control.

99% confidence
A

ES6 class syntax provides clean OOP without prototype manipulation. Factory creation simplified: class User {constructor(name, email) {this.name = name; this.email = email;}}. Factory: class UserFactory {static create(type, data) {switch(type) {case 'admin': return new User(data.name, data.email, 'admin'); case 'customer': return new User(data.name, data.email, 'customer');}}}. Benefits: Clear inheritance (extends), constructor methods, instanceof checks, super keyword for parent calls. Pre-ES6: Complex prototype chains: function User() {}. User.prototype.create = ... ES6 classes make factory patterns more readable with standard OOP syntax. Combined with static methods for factory functions. TypeScript enhances: Generic factories: class Factory {static create(constructor: new() => T): T {return new constructor();}}.

99% confidence
A

Destructuring eliminates Builder parameter order confusion, spread operator merges configurations. Pattern: function createUser({name, email, age = 18, role = 'user'} = {}) {return {name, email, age, role};}. Usage: createUser({name: 'John', email: '[email protected]'}) - no need for UserBuilder.setName().setEmail().build(). Spread for merging defaults: const defaults = {theme: 'light', lang: 'en'}; const config = {...defaults, theme: 'dark', lang: 'fr'} - overrides specific properties. Combination: function createRequest(baseConfig, overrides) {return {...baseConfig, ...overrides};}. Benefits: No parameter order constraints, optional parameters without nulls, partial object updates, immutable pattern. TypeScript type safety: interface Options {name: string; email?: string; age?: number;}. Use destructuring when: 3-5 parameters, some optional, no complex validation needed. Use traditional Builder when: >5 parameters, need step-by-step validation, require immutability guarantees.

99% confidence
A

Arrow functions enable concise factory functions and closure-based patterns. Factory functions: const createUser = (name) => ({name, id: generateId(), createdAt: new Date()}); - returns objects without class boilerplate. Closure-based singleton: const createSingleton = (fn) => {let instance; return () => instance || (instance = fn());}; const getConfig = createSingleton(() => ({apiUrl: '...', timeout: 5000})); - first call creates instance, subsequent calls return same. Higher-order factories: const createLogger = (level) => (msg) => console.log([${level}] ${msg}); const infoLog = createLogger('INFO'); infoLog('User logged in'). Benefits: No this binding issues, implicit return, lexical scope capture, function composition. Use arrow functions for: Simple factories (parameter + object creation), closure-based state management (counters, caches), functional composition patterns. Avoid when: Need constructor semantics (instanceof), prototype methods, complex object hierarchies.

99% confidence
A

ES6+ features replace classical patterns with built-in language capabilities. ES6 modules eliminate Singleton (automatic caching), class syntax simplifies Factory (no prototype chains), destructuring eliminates Builder for simple cases, spread operator eliminates Object.assign for Prototype cloning, structuredClone eliminates custom deep clone methods, async/await eliminates Async Factory patterns. Pattern eliminations: Singleton → ES6 modules, Builder → destructuring/spread, Factory Method → arrow functions/closures, Prototype → structuredClone/Object.assign, Multiton → Map/WeakMap, Abstract Factory → dynamic imports + conditional exports. Example: Instead of Prototype clone method: const clone = structuredClone(original). Instead of Abstract Factory: const module = await dynamicImport(./adapters/${type}.js); const adapter = module.createAdapter(). Keep classical patterns when: Need complex validation, step-by-step construction, polymorphic hierarchies, framework integration. Rule: Use language features first, add patterns only for specific requirements beyond built-in capabilities.

99% confidence
A

Test Factory by mocking the factory itself and testing creation logic. Pattern: const mockFactory = {create: jest.fn(() => mockUser)}; const service = new UserService(mockFactory); await service.createUser('admin'); expect(mockFactory.create).toHaveBeenCalledWith('admin', expectedData). Test factory independently: expect(UserFactory.create('admin')).toBeInstanceOf(AdminUser); expect(UserFactory.create('customer')).toBeInstanceOf(CustomerUser). Mock different creation scenarios: jest.spyOn(UserFactory, 'create').mockImplementationOnce(() => new MockUser()). Test error handling: expect(() => UserFactory.create('invalid')).toThrow('Invalid user type'). Integration test: Verify factory registers correct types in DI container. Use dependency injection to inject factory into service, enabling complete mocking. Test factory's internal dependencies by mocking external services it uses for object creation. Key: Test both factory behavior (creates correct objects) and factory integration (service uses factory correctly).

99% confidence
A

Test Builder by verifying fluent API chain and final object state. Pattern: const user = new UserBuilder().setName('John').setEmail('[email protected]').setAge(30).build(); expect(user.name).toBe('John'); expect(user.email).toBe('[email protected]'); expect(user.age).toBe(30). Test each step individually: const builder = new UserBuilder(); expect(builder.setName('John')).toBe(builder); // fluent return. Test validation: expect(() => new UserBuilder().build()).toThrow('Name required'); expect(() => new UserBuilder().setName('John').build()).not.toThrow(). Test immutability: const built = builder.build(); expect(() => built.name = 'Jane').toThrow(); // readonly. Test building multiple objects: const user1 = builder.build(); const user2 = builder.build(); expect(user1).not.toBe(user2); // new instances. Test builder state reset after build(): some builders reset, others maintain state - document behavior. Use snapshot testing for complex objects: expect(built).toMatchSnapshot();. Test invalid combinations: expect(() => builder.setAdmin(true).setGuest(true).build()).toThrow('Cannot be both admin and guest').

99% confidence
A

Singleton testing requires instance isolation between tests. Problem: Shared state causes test pollution. Fix 1: Reset cache: Singleton['instance'] = null (TypeScript private access) in afterEach(). Fix 2: Use dependency injection: class UserService {constructor(private db: Database)} - inject singleton via DI container, override in tests with mock. Fix 3: Module mocking: jest.mock('./database', () => ({Database: MockDatabase})). Test singleton behavior: const instance1 = Database.getInstance(); const instance2 = Database.getInstance(); expect(instance1).toBe(instance2); Test lazy initialization: expect(Database.createCalled).toBe(false); const db = Database.getInstance(); expect(Database.createCalled).toBe(true); Test thread safety (async): const promises = [Database.getInstance(), Database.getInstance(), Database.getInstance()]; const results = await Promise.all(promises); expect(results[0]).toBe(results[1]); expect(results[1]).toBe(results[2]); Best approach: Avoid Singleton pattern, use DI with singleton scope - framework manages lifecycle and test isolation.

99% confidence
A

Integration tests verify creational patterns work together in real scenarios. Factory integration: Test full flow from client request to created object usage. Pattern: POST /users → UserController → UserFactory → Database. Verify object created with correct type, saved to database, returned in response. Test factory with real dependencies: UserFactory depends on Database and EmailService - verify both are called correctly. Builder integration: Test complex object creation flow. Example: ConfigBuilder reads environment variables, validates, creates final Config object used by Application. Test with actual .env file, verify all configurations applied. Prototype integration: Test cloning with real data structures. const original = new ComplexObject(nestedData); const clone = original.clone(); modify clone, verify original unchanged. Object Pool integration: Test pool behavior under load. Create multiple objects, return some to pool, verify reuse. Test pool limits: pool.acquire() x10, pool.acquire() should wait/block when empty. Test persistence: Objects in pool survive between requests. Integration testing focuses on pattern behavior with real dependencies, not just unit behavior.

99% confidence
A

Over-engineering uses complex patterns for simple object creation, creating unnecessary abstractions and premature optimization. Red flags (2025): Builder for 2-3 parameters, Factory for single implementation, Singleton for stateless utility, Abstract Factory for 2 types, 10-deep inheritance chains to follow business logic. Symptoms: Need to jump through multiple files to understand simple object creation, service layers become overly deep, more boilerplate than business logic. Avoid by: (1) Composition over inheritance (2025 principle), (2) Parse don't validate - use TypeScript types instead of runtime validators, (3) Keep service layers shallow - minimize abstraction layers, (4) Use language features first (destructuring for optional params, ES6 modules for singletons, spread for defaults). Example: Over-engineered: class UserBuilder {setName() {...} setEmail() {...} setAge() {...} build() {return new User(...)}}. Simple: function createUser({name, email, age = 18}: UserOptions): User {return {name, email, age, id: generateId()};}. Decision framework: Is this solving a real problem NOW (not hypothetical future)? Does pattern reduce complexity or add it? If pattern adds more lines than it saves, remove it. TypeScript best practice (2025): Leverage type system for safety, avoid runtime pattern complexity.

99% confidence
A

Generics add type safety to factories and builders but require careful design. Factory with generics: class Factory {static create(constructor: new() => T): T {return new constructor();}}. Usage: const user = Factory.create(User); - type-safe creation. Generic Builder: class Builder {private data: Partial = {}; set(key: K, value: T[K]) {this.data[key] = value; return this;} build(): T {return this.data as T;}}. Usage: new Builder().set('name', 'John').set('age', 30).build(). Generic constraints: create(data: T): T. Generic registries: class Container {private items = new Map<string, any>(); register(key: string, item: T): void {this.items.set(key, item);} get(key: string): T {return this.items.get(key);}}. Pitfalls: Complex generic signatures can be hard to read, generic constraints may limit flexibility, type inference sometimes fails. Best practice: Use generics for type safety, but provide clear documentation. Keep generics simple - avoid overly complex constraints unless necessary.

99% confidence
A

Memory leaks occur when Singletons hold strong references preventing garbage collection. Common causes (2025): Event singleton registering listeners never removed, cache storing objects indefinitely, closures capturing large contexts, DOM references in long-lived objects. Prevention strategies: (1) Use WeakMap/WeakSet for temporary storage - holds weak references allowing GC when keys become unreachable. Pattern: const cache = new WeakMap(); cache.set(userObj, metadata); // GC collects when userObj unreachable. (2) Remove event listeners explicitly: class EventSingleton {dispose() {this.listeners.forEach(({element, type, handler}) => element.removeEventListener(type, handler));}}. (3) Implement size-limited caches with LRU eviction. (4) Clear closures after use - avoid capturing entire contexts. (5) Use dispose/cleanup methods called during app shutdown. Testing (2025): Chrome DevTools Memory Profiler for heap snapshots, look for detached DOM nodes, increasing memory between snapshots. Modern solution: Avoid Singleton entirely - use DI with singleton scope providing lifecycle hooks (onModuleInit, onModuleDestroy in NestJS). WeakMap advantage: Manages metadata without preventing GC, automatically cleans when keys collected. Best practice: Prefer DI over Singleton for better lifecycle management and testability.

99% confidence
A

TypeScript Factory pitfalls (2025): (1) Generic type inference failures - Factory.create() returns unknown. Fix: Explicit type parameters: create(type: string): T or use constructor parameter: create(ctor: new() => T): T. (2) instanceof fails with interfaces - interfaces are compile-time only, erased at runtime. Fix: Use discriminated unions with type property or type guards with runtime checks. (3) Constructor signature constraints too restrictive. Problem: new() doesn't allow parameters. Fix: new(...args: any[]) => T for flexible constructors. (4) Return type widening to any/unknown loses type safety. Fix: Explicit return type annotations: function create(): User {return new User();}. (5) String-keyed registries lose type information. Fix: Use const enums or symbol keys: const USER_TYPE = Symbol('User'); registry.register(USER_TYPE, User). (6) Generic constraints failing type resolution - complex generics confuse inference. Fix: Simplify constraints or use helper types. (7) Variance issues with generic factories - covariance/contravariance mismatches. Advanced pattern (2025): Use mapped types for type-safe factory registries: type FactoryMap = {user: User; admin: Admin}; function create(type: K): FactoryMap[K]. Leverage TypeScript's type system for compile-time safety, avoid runtime as any casts.

99% confidence
A

TypeScript testing strategies (2025): (1) Mock factories with jest.spyOn() - Microsoft DevBlogs best practice for Factory pattern: jest.spyOn(UserFactory, 'create').mockReturnValue(mockUser). Overwrite factory methods to return test objects. (2) Use jest.mocked() for typed mocks: const mockFactory = jest.mocked(UserFactory) - preserves type safety in mocks. (3) Test type safety at compile-time: Use ts-expect-error to verify invalid usage fails: // @ts-expect-error - should not accept string. factory.create(123). (4) Mock with proper interfaces satisfying contracts: class MockUserFactory implements IUserFactory {create(): User {return mockUser;}}. (5) Test Builder fluent chains: Verify each method returns this, test validation throws appropriately, snapshot test final built objects. (6) Singleton testing: Reset instance between tests via private field access or use DI for test isolation. (7) Integration tests with real dependencies: Test Factory with actual database, Builder with validation services. Jest + TS-Jest (2025): Seamless TypeScript support without Babel config. Alternative: Vitest offers cleaner TypeScript + JSX support, less overhead. Testing focus: Behavior over implementation, type safety verified at compile-time, runtime behavior tested via Jest. Keep mocks simple, prefer real implementations in integration tests.

99% confidence