Creational Patterns in Clojure

Understand how classic creational patterns translate into Clojure's function-first, data-oriented style.

Creational patterns are techniques for constructing values, resources, or collaborators in a controlled way. In object-oriented languages, that usually means deciding which class to instantiate. In Clojure, the same concern still exists, but it usually appears in a smaller, data-oriented form: choosing how to build maps, records, functions, delayed resources, or system maps without hard-wiring the rest of the program to one concrete construction path.

That difference matters. If you read the Gang of Four patterns too literally, Clojure can look like it “doesn’t need” creational patterns at all. That is not quite right. The patterns still exist, but many of them collapse into simpler tools:

  • a factory often becomes an ordinary function
  • a builder often becomes a sequence of pure map transformations
  • dependency injection often becomes explicit parameters or closures
  • singleton-like behavior often becomes defonce, delay, or a clearly scoped runtime resource

The important question is not, “How do I port a Java pattern into Clojure?” It is, “What construction problem do I have, and what is the smallest Clojure-shaped tool that solves it cleanly?”

What Changes In Clojure

Three language traits change the shape of creational patterns immediately.

First, data is cheap and central. Many Clojure programs model the world with maps, vectors, sets, records, and plain values rather than rich object hierarchies. That means “construction” often means normalizing data, filling defaults, validating shape, or selecting a function.

Second, functions are first-class. A dependency can simply be another function or a map of functions. You often do not need a framework, container, or abstract factory when you can pass behavior directly.

Third, immutable values are the default. Once a value exists, you normally derive a new value instead of mutating the old one. So a builder-style workflow often looks like a pipeline of assoc, update, validation, and final assembly rather than a mutable object with setters.

The Main Questions In This Chapter

Most creational choices in Clojure reduce to a small set of design questions:

  • Do you need one canonical way to create a family of related values?
  • Do you need to build a large configuration or domain value incrementally?
  • Do you need to inject runtime dependencies without hiding them?
  • Do you need to delay creation of an expensive resource until the program actually uses it?
  • Do you need to share or cache reusable structures safely?

The diagram below shows the practical mapping for this chapter.

    flowchart TD
	    Problem["Construction problem"] --> Variants["Multiple variants or construction rules"]
	    Problem --> Incremental["Large value built in stages"]
	    Problem --> Runtime["Runtime dependency or resource wiring"]
	    Problem --> Shared["Shared or cached creation result"]
	
	    Variants --> Factory["Factory function"]
	    Incremental --> Builder["Builder with functions and maps"]
	    Runtime --> DI["Dependency injection with higher-order functions"]
	    Runtime --> Lazy["Lazy initialization with delay or force"]
	    Shared --> Registry["Registry or flyweight"]
	    Shared --> Singleton["Singleton-like runtime resource"]

When Classic Names Still Help

The classic names are still useful because they help you communicate recurring trade-offs:

  • “factory” means callers should not know the full creation logic
  • “builder” means the value is easier to assemble in stages than in one literal
  • “singleton” usually signals a shared runtime resource and all the risks that come with hidden global access
  • “flyweight” signals memory reuse and shared representation
  • “registry” signals indirection through a shared lookup table

Those names are valuable as long as you do not let them smuggle in unnecessary ceremony. A ten-line function that returns a normalized map may be a perfectly good factory. A threaded map pipeline may be a perfectly good builder. The pattern matters because of the construction problem it solves, not because you reproduced the class diagram from another language.

A Practical Selection Guide

Use a factory function when callers need a stable entry point and you want to hide defaults, validation, variant choice, or normalization.

Use a builder-style pipeline when a value has many optional fields or staged derivations and one giant literal would be hard to read or validate.

Use dependency injection via functions or maps when a component should be testable, swappable, and explicit about what it needs from the outside world.

Use lazy initialization when a resource is expensive and should not be created at startup unless it is actually used.

Use registry and flyweight patterns carefully. They can solve real lookup and memory problems, but they also make access more indirect. If plain explicit parameters are enough, prefer them.

Treat singletons with skepticism. In Clojure, a request for a singleton often means one of two things:

  • you really do have one shared runtime resource such as a connection pool or metrics registry
  • you are about to hide global state that should have stayed explicit

The Clojure Test

Before applying any creational pattern in this chapter, ask:

  1. Can I solve this with plain data and one ordinary function?
  2. Does the pattern make construction clearer, or does it only add indirection?
  3. Will this choice make testing easier or harder?
  4. Am I creating a stable boundary, or am I hiding global state?

That test keeps the chapter grounded. The goal is not to force every classic creational label into Clojure. The goal is to build values and resources in a way that matches Clojure’s strengths: explicit data, small functions, controlled state, and composable boundaries.

Loading quiz…
Revised on Thursday, April 23, 2026