Builder
A constructor with six arguments looks fine until you read new Coffee(12, true, false, 2) and cannot tell which value is the milk. Swap the two booleans and it still compiles, runs, and quietly brews the wrong drink.
- ▸A six-argument constructor and six named steps do the same work, but only one survives a swapped boolean.
- ▸The finished object does not exist until the closing call runs, so the half-configured, possibly invalid version never escapes.
- ▸Once construction is a list of steps, the same list can assemble entirely different results.
Picture an order screen that creates a coffee. The first version takes a size and nothing else. Then someone needs milk, so a second version adds a boolean. Then decaf, another boolean. Then extra shots, an integer. Six months later the call reads new Coffee(12, true, false, 2), and at the call site every value is a guess.
The size is 12, fine. But true, false could be milk-then-decaf or decaf-then-milk, and the compiler has no opinion either way. The 2 might be shots or sugars. Nothing names these values where you read them; you have to jump to the signature and count positions to know what is what.
The dangerous part is the matching types. Swap the two booleans and the program still compiles, still runs, and silently brews a decaf when you wanted milk. No error, no warning, just a wrong drink handed to a customer. Adding one more option means yet another stacked version of the call, and readability drops with every parameter you bolt on.