Symptom

Multiple ways to construct an object — overloaded constructors, factory methods, copy helpers — each repeat field assignments, defaults, and validation. Adding a new field forces edits in every construction path, and a field missed in one path silently produces partly-initialized objects. The team's compounds across paths no test inspects together.

Goal

A single canonical constructor owns all field assignment and validation. Every other construction path is a one-line delegation that supplies the variant-specific arguments. Adding a new field is a one-place change; the team's drops from N parallel edits to one, and between construction-as-orchestration and construction-as-initialization becomes structural.

Before the refactoring

class Loan {
static newTermLoan(commitment, customer, maturity) {
const loan = new Loan();
if (commitment < 0) throw new Error('commitment must be non-negative');
loan.commitment = commitment;
loan.customer = customer;
loan.maturity = maturity;
loan.expiry = null;
loan.unusedPercentage = 0.0;
return loan;
}
static newRevolver(commitment, customer, expiry) {
const loan = new Loan();
if (commitment < 0) throw new Error('commitment must be non-negative');
loan.commitment = commitment;
loan.customer = customer;
loan.maturity = null;
loan.expiry = expiry;
loan.unusedPercentage = 1.0;
return loan;
}
static newAdvisedLine(commitment, customer, expiry) {
const loan = new Loan();
if (commitment < 0) throw new Error('commitment must be non-negative');
loan.commitment = commitment;
loan.customer = customer;
loan.maturity = null;
loan.expiry = expiry;
loan.unusedPercentage = 0.5;
return loan;
}
}

After the refactoring

class Loan {
constructor(commitment, customer, expiry, maturity, unusedPercentage) {
if (commitment < 0) throw new Error('commitment must be non-negative');
this.commitment = commitment;
this.customer = customer;
this.expiry = expiry;
this.maturity = maturity;
this.unusedPercentage = unusedPercentage;
}
static newTermLoan(commitment, customer, maturity) {
return new Loan(commitment, customer, null, maturity, 0.0);
}
static newRevolver(commitment, customer, expiry) {
return new Loan(commitment, customer, expiry, null, 1.0);
}
static newAdvisedLine(commitment, customer, expiry) {
return new Loan(commitment, customer, expiry, null, 0.5);
}
}
Example source: Adapted from Joshua Kerievsky's Loan-class example in Refactoring to Patterns (Addison-Wesley, 2004), chapter 6. The Java original used overloaded constructors; this JavaScript translation uses static creation methods delegating to one canonical constructor — same shape, same single-point-of-initialization payoff.
Pressure

Constructor duplication is Shotgun Surgery on the field set of a class. Each new variant adds a parallel copy of the initialization rules; subtle divergence between paths is easy to miss in review and easy to ship to production unnoticed. The team's compounds across every variant that has its own way to forget a field.

Tradeoff

Chaining couples every construction path to the canonical constructor's parameter list, and that list grows as variants accumulate. Call sites pass null or default sentinels for parameters that don't apply to their variant, eroding intention-revealing names at the call site. The of decoding which parameter applies to which variant rises with the signature's length.

Relief

One place to read what 'constructing a Loan' means; new fields land in one constructor; each variant reads as the parameters it cares about. Validation runs once per construction, not once per path. The team's per construction site drops because the canonical body is the only authority, and review focuses on the variant-specific argument list.

Trap

The canonical constructor grows into a Long Parameter List as variants accumulate. Readers no longer know which parameters matter for which variant, and the centralization stops paying — at that point Replace Constructors With Creation Methods or a parameter object becomes the next move. The cure adds when the canonical signature grows beyond what the variant set justifies.