# Replace Constructor with Factory Method

> Wrap object creation in a named static or module function when a plain constructor cannot express intent, pick a subtype, or reuse instances.

- kind: technique
- category: simplifying-method-calls
- aliases: Replace Constructor with Factory Function
- canonical: https://aipatterns.dev/refactoring/techniques/replace-constructor-with-factory-method
- inspired-by: https://refactoring.guru/replace-constructor-with-factory-method

## Problem

A constructor has to do more than a constructor can express well. Callers pass a flag to get different variants, several constructors differ only by argument order, or creation should sometimes return a cached instance or a subclass. `new` always returns a fresh object of exactly one class, with no name to explain which variant you get.

## Solution

**Create a static method (or module-level function) that builds the object, give it a name that describes the variant, make the constructor private, and route callers through the factory.**

## Before

```ts
class Discount {
  constructor(
    readonly kind: "percent" | "fixed",
    readonly value: number,
    readonly currency?: string,
  ) {
    if (kind === "percent" && (value <= 0 || value > 100)) throw new RangeError("bad percent");
    if (kind === "fixed" && !currency) throw new Error("fixed discount needs a currency");
  }
}

const promo = new Discount("percent", 15);
const voucher = new Discount("fixed", 2000, "EUR");
```

## After

```ts
class Discount {
  private constructor(
    readonly kind: "percent" | "fixed",
    readonly value: number,
    readonly currency?: string,
  ) {}

  static percent(rate: number): Discount {
    if (rate <= 0 || rate > 100) throw new RangeError("bad percent");
    return new Discount("percent", rate);
  }

  static fixed(amountCents: number, currency: string): Discount {
    return new Discount("fixed", amountCents, currency);
  }
}

const promo = Discount.percent(15);
const voucher = Discount.fixed(2000, "EUR");
```

## Why

- Each way of creating the object has a name and only the arguments it needs.
- Invalid combinations (a percent with a currency, a fixed amount without one) cannot be written.
- The factory can later return a subclass, a cached instance or a result type, without changing callers.

## Steps

1. Create a static factory that calls the existing constructor with the same arguments. Run the tests.
2. Replace `new` calls with the factory, one module at a time. Run the tests.
3. Make the constructor `private` (or unexported). The compiler now finds any missed `new` calls.
4. Split the factory by variant, moving variant-specific validation into each one. Run the tests.

## Watch out

- Do not add a factory when a plain constructor with clear parameters works. `new Point(x, y)` needs no `Point.of`.
- Private constructors block subclassing in some languages and break frameworks that instantiate classes reflectively (ORMs, DI containers, serializers). Check before you hide the constructor.
- A factory that grows a `switch` over a type string is just moving the problem. Use [Replace Type Code with Subclasses](https://aipatterns.dev/refactoring/techniques/replace-type-code-with-subclasses.md) and let each subclass have its own factory, or a map.
- The inverse, replacing a factory with a constructor, has no named technique. Do it when the factory adds nothing beyond `new`.

## Related

- Treats [Switch Statements](https://aipatterns.dev/refactoring/smells/switch-statements.md) and [Primitive Obsession](https://aipatterns.dev/refactoring/smells/primitive-obsession.md) on type-code constructor arguments.
- Supports [Remove Setting Method](https://aipatterns.dev/refactoring/techniques/remove-setting-method.md) by centralizing initialization.
- Not the same as the [Factory Method](https://aipatterns.dev/design-patterns/factory-method.md) pattern, which uses subclass overrides. See also [Abstract Factory](https://aipatterns.dev/design-patterns/abstract-factory.md).

## Agent checklist

- [ ] The constructor needed a flag, had ambiguous overloads, or must return something other than a fresh instance.
- [ ] Each factory has a name that describes the variant it creates.
- [ ] No framework requires the constructor to stay public.
- [ ] All `new` calls outside the class were migrated before the constructor went private.
