JavaScript Factory Pattern for Dynamic Objects

The Factory Pattern is a creational design pattern in JavaScript that provides an interface for creating objects without specifying their exact classes upfront. Instead of invoking a constructor directly using the new keyword, a factory function handles the instantiation logic dynamically based on runtime parameters or application state. This article explains how the pattern works, demonstrates dynamic object creation with clear code examples, and outlines when to use it to keep your codebase modular and maintainable.


Understanding the Factory Pattern

In JavaScript, a factory is simply a function that returns a new object. Unlike standard constructor functions or ES6 classes, a factory function encapsulates the instantiation logic, abstracting the creation process away from the caller.

When applications require different types of objects depending on user input, configuration, or API responses, hardcoding constructor calls creates tight coupling. The Factory Pattern solves this by delegating the decision of which object to instantiate to the factory itself.


How Dynamic Object Creation Works

Dynamic creation occurs when a factory function evaluates arguments or runtime conditions to decide the shape, prototype, or class of the returned object.

Here is a practical implementation creating different notification services dynamically:

// Define distinct product classes
class EmailNotification {
  send(message) {
    return `Sending Email: "${message}"`;
  }
}

class SMSNotification {
  send(message) {
    return `Sending SMS: "${message}"`;
  }
}

class PushNotification {
  send(message) {
    return `Sending Push Notification: "${message}"`;
  }
}

// Factory function
function createNotificationService(type) {
  const providers = {
    email: EmailNotification,
    sms: SMSNotification,
    push: PushNotification,
  };

  const ProviderClass = providers[type.toLowerCase()];

  if (!ProviderClass) {
    throw new Error(`Notification type "${type}" is not supported.`);
  }

  return new ProviderClass();
}

// Dynamic instantiation at runtime
const userPreference = "sms"; // Determined dynamically at runtime
const notifier = createNotificationService(userPreference);

console.log(notifier.send("Your order has shipped!"));
// Output: Sending SMS: "Your order has shipped!"

In this example, the calling code does not need to know about SMSNotification or EmailNotification. It only provides a key ("sms"), and the factory dynamically returns the appropriate instance.


Using Factory Functions Without Classes

JavaScript’s flexible object model allows factories to create and return plain object literals with closures, avoiding classes entirely:

function createUser(role, name) {
  const basePermissions = ['read'];

  const roleBehaviors = {
    admin: {
      permissions: [...basePermissions, 'write', 'delete'],
      dashboard: () => `Rendering Admin Dashboard for ${name}`,
    },
    guest: {
      permissions: basePermissions,
      dashboard: () => `Rendering Guest View for ${name}`,
    },
  };

  const selectedRole = roleBehaviors[role] || roleBehaviors.guest;

  return {
    name,
    role,
    permissions: selectedRole.permissions,
    render: selectedRole.dashboard,
  };
}

const admin = createUser('admin', 'Alice');
const guest = createUser('guest', 'Bob');

console.log(admin.render()); // Rendering Admin Dashboard for Alice
console.log(guest.permissions); // ['read']

Key Benefits


When to Use the Factory Pattern

Use the Factory Pattern when: * An application handles multiple variations of an object that share a common interface. * The exact type of object needed can only be determined at runtime (e.g., via user input, environment variables, or remote data). * Object creation requires complex configuration or data transformation that shouldn’t be duplicated across the codebase.