Factory Method
A planRoute method opens with new Truck(), and so does the mobile endpoint, the admin re-run button, and the retry job. The day sea freight arrives, every one of those files has to learn the word Ship.
- ▸Scatter
new Truck()across callers and a new vehicle edits them all; behind one overridable method, it edits none. - ▸The win isn't deleting
new; it's moving the one line that picks the class so callers name none. - ▸
Collection.iterator()ships this in the JDK: each collection returns its own iterator, your loop never names the class.
A planDelivery method starts innocent. It works out a route and books the vehicle in one line:
class Logistics { planDelivery(order) { const truck = new Truck(); // the one line that names a class truck.load(order); return truck.route(order.from, order.to); } }
That new Truck() looks harmless. The trouble is it isn't alone. The mobile endpoint books a truck. The admin 're-run delivery' button books a truck. A nightly retry job books a truck. Each one wrote new Truck() for itself.
Then the company adds sea freight. Now every place that built a truck sometimes needs a ship instead, so each grows the same branch:
const vehicle = order.bySea ? new Ship() : new Truck();
The decision 'which class do I build' is now copy-pasted into every caller, and the day air freight arrives you edit all of them again. The actual work, picking a vehicle for an order, is one decision. The code has scattered that one decision across every file that ever needed a vehicle. Each caller is welded to the concrete classes instead of to the job it wanted: get a vehicle and use it.