skip to content

What is a prototype registry, and when would you prefer registering configured prototypes over adding another factory or subclass?

level: middleimportance: should knowfreq 40%

answer

  1. Registry = key → configured exemplar, fetch and clone
  2. Variants as data, not code
  3. Behaviour differs → factory; state differs → prototype
  4. Runtime key errors, global mutable catalogue
  5. Hand out clones, never the exemplar itself

basics

~20 s

A prototype registry is a lookup table from a key to a ready-made, configured object. Clients ask for a key and clone what comes back. You prefer it when new variants differ only in configuration, so adding one means adding data, not code.

solid answer

~50 s

A prototype registry (prototype manager) is a keyed catalogue of pre-configured exemplars: `registry.register("warning-dialog", configuredDialog)` and later `registry.get("warning-dialog").clone()`. It shifts variant selection from *code* to *data*. With factories, each new product type typically means a new branch in a switch, a new factory method, or a new subclass — the creation code must be recompiled and redeployed to learn a new variant. With a registry, a variant is an instance, so it can be loaded from configuration, a database, a plugin, or built by an end user at runtime and then registered. That is why graphical editors, workflow engines, game entity systems and rule engines lean on it. The costs: the registry is mutable global-ish state that needs lifecycle and thread-safety discipline; a prototype's correctness now depends entirely on its clone semantics; and you lose compile-time type distinctions, so a bad key becomes a runtime error rather than a compile error. Prefer a factory when variants differ in *behaviour* (different classes/algorithms), a registry when they differ in *state*.

code

pseudocode · 12 lines
pseudocode
class PrototypeRegistry {
  Map<String, Prototype> items = {}
  void register(String key, Prototype p) { items[key] = p }
  Prototype create(String key) {
      p = items[key] ?? throw UnknownPrototype(key)
      return p.clone()          // never return the exemplar itself
  }
}

// variants arrive as data, at runtime
for (def in loadConfig("entities.json"))
    registry.register(def.name, buildFrom(def))

go deeper

for a junior

Define it as a key→object catalogue whose entries you clone, with one example such as a shapes palette.

for a middle

Contrast variants-as-data with variants-as-classes and name the trade-offs: runtime keys, global state, clone correctness.

for a senior

Discuss lifecycle and publication, immutability of exemplars, catalogue validation, versioning of persisted prototypes, and combining a factory façade with a registry behind it.

for a principal

Frame it as moving an extension point from build time to runtime — with the governance that implies: who may register, how the catalogue is validated and versioned, and how behavioural variation is kept in code where it belongs.

### Definitions - **Factory Method**: a creational pattern where a method (often overridden per subclass) decides which concrete class to instantiate. Variants = classes. - **Simple/static factory**: one function that switches on a key and calls the right constructor. Variants = branches. - **Prototype registry / prototype manager**: a map `key → prototype instance`. Callers fetch and clone. Variants = registered instances. ### How the registry works ``` registry = {} registry["tree"] = configure(new Sprite(), leafTexture, collider=true, hp=20) registry["rock"] = configure(new Sprite(), rockTexture, collider=true, hp=99) spawn(key, at) { e = registry[key].clone(); e.position = at; world.add(e); } ``` The client names a *string* (or enum, or id) and never names a class. Registration can happen anywhere: at startup, from a config file, from a plugin discovered at runtime, or from a designer's editor session. ### The decisive question: do variants differ in behaviour or in state? - **Different behaviour** — genuinely different algorithms, different method implementations, different invariants. You need different *classes*. Use a factory (or Abstract Factory for families, or Strategy for pluggable behaviour). Cloning an exemplar cannot conjure new code. - **Different state/configuration** — same class, different field values (texture, thresholds, labels, timeouts, styling). A registry of exemplars is a better fit: no class explosion, and new variants require no code change. Many systems that started with a subclass per variant (`GoblinArcher`, `GoblinChief`, `EliteGoblinArcher`, …) are really in the second category and collapse dramatically when converted to data-driven prototypes. ### What the registry buys you 1. **Open/closed at runtime.** New variants without recompiling — the extension point is a registration call, not a source edit. 2. **User-authored variants.** A user configures an object in the UI (a brush, a report template, a form layout) and saves it as a prototype; "new from template" is `clone()`. 3. **Fewer classes.** No hierarchy whose only purpose is to hold different constant values. 4. **Cheap repetition of expensive setup.** The exemplar paid the parsing/loading/precomputation cost once. 5. **Uniform client code.** One code path for all variants; adding a variant cannot break the client. ### What it costs you 1. **Runtime instead of compile-time errors.** `registry.get("warnign-dialog")` fails at run time. Mitigate with a validated key type, startup validation of the whole catalogue, or code-generated key constants. 2. **Global mutable state.** Who registers? When? Can something be re-registered or removed mid-flight? A registry needs an explicit lifecycle and, if concurrent, thread-safe reads and a defined publication point (usually: fill it during bootstrap, then treat it as read-only). 3. **Shared-prototype mutation hazard.** If a caller obtains the prototype and mutates it instead of a clone, every subsequent clone inherits the change. Return clones only, or keep prototypes immutable. 4. **Correctness rests on clone semantics.** A shallow clone means all spawned objects share nested state with the exemplar and with each other — a spectacular class of bug at scale. 5. **Discoverability.** A catalogue keyed by strings is harder to navigate than a type hierarchy; tooling ("find usages") stops helping. 6. **Versioning.** Prototypes persisted to disk/database are effectively a schema; changing the class means migrating stored exemplars. ### Combining rather than choosing These are not exclusive. A common arrangement: a **factory façade** that clients call, backed internally by a prototype registry for configuration-only variants and by constructors/subclasses for behavioural variants. Clients keep a stable creation API while the mechanism behind each key varies. Builder pairs naturally too: use a Builder once to construct each exemplar with full validation, register it, then clone thereafter. ### Smells that you picked wrong - A registry whose exemplars are all default-constructed → the copying buys nothing; use a factory. - A subclass hierarchy in which every subclass only overrides constant-returning getters → you wanted a registry. - Clones being re-configured heavily at every call site → the exemplars are not actually the right granularity.

  • What breaks if the registry returns the stored prototype instead of a clone?
    Callers mutate the exemplar, so every future clone inherits their changes — a global, order-dependent corruption that is very hard to trace. Either always clone on read, or make prototypes immutable so mutation is impossible.
  • How do you keep string keys from becoming a source of runtime failures?
    Validate the whole catalogue at startup (every referenced key resolves), use a typed key/enum or generated constants where the set is known at build time, and fail fast and loudly on an unknown key rather than returning null. For plugin-supplied keys, validate at registration time and surface the catalogue in tooling.

A tailor's shop of sample garments versus a book of sewing patterns. The sample rack (registry) lets you point at a finished jacket and say "one like that" — adding a new style means hanging another sample. The sewing patterns (factories) encode how to make something new; you need them when the new garment is genuinely constructed differently, not just in another colour.

saying these in an interview costs you the question

  • Believing a prototype registry can introduce new *behaviour* — cloning cannot produce new code, only new state.
  • Handing out the stored prototype rather than a copy.
  • Treating the registry as free of lifecycle concerns: unclear registration timing, mutation after publication, no thread-safety.
  • Creating a subclass per configuration variant when the class body only returns different constants.
  • Assuming a registry replaces factories entirely rather than often sitting behind one.

context