A widget toolkit has two themes. Every button, input and panel comes in both, and mixing them looks broken.
Factory Method defers one choice. This defers a whole coordinated set, and the coordination is the entire reason it exists.
The pattern
One object with a creation method per product, and one implementation per family.
interface WidgetKit { createButton(): Button; createInput(): Input; createPanel(): Panel;}
class DarkKit implements WidgetKit { createButton() { return new DarkButton(); } createInput() { return new DarkInput(); } createPanel() { return new DarkPanel(); }}Application code takes a WidgetKit and never names a concrete class:
const kit = new DarkKit(); const button = kit.createButton(); const input = kit.createInput(); const panel = kit.createPanel();
One factory, one family — you cannot get a dark button next to a light input.
The trade it makes, and it is a real one
Adding a family is easy: one new class, nothing else changes.
Adding a product is not: createTooltip() goes on the interface and every
existing kit must implement it. Two families is two edits; ten is ten, in ten
files, and the compiler will at least tell you about all of them.
In this language, it can be an object
The interface has no state and no behaviour beyond construction, so:
type WidgetKit = { createButton: () => Button; createInput: () => Input; createPanel: () => Panel;};
const darkKit: WidgetKit = { createButton: () => new DarkButton(), createInput: () => new DarkInput(), createPanel: () => new DarkPanel(),};Same guarantee, no classes. TypeScript will still refuse a kit that forgets a method, which is the part that was doing the work.
Where you have met it
DocumentBuilderFactory and friends in Java’s XML stack. Cross-platform UI
toolkits. Database drivers, where a connection creates statements and result
sets that must all belong to the same dialect — that last one is the clearest
real example, because a Postgres Connection handing you a MySQL
PreparedStatement would be nonsense.