I. Prototype Pattern — Creational Pattern
The Prototype pattern specifies the kinds of objects to create using a prototypical instance, and creates new objects by cloning that prototype.
Instead of building a new object from scratch every time, you start from a pre-built, fully-initialized instance and copy it. The clone is then free to be modified independently — mutations to the copy never affect the original prototype.
When to use it:
- Object initialization is expensive (database lookups, file reads, complex calculations)
- You need many objects that differ only in a few fields
- You want to avoid subclassing just to configure objects differently
- The class to instantiate is only known at runtime
II. Real-world Example
Imagine a web page generator. Every page on the site shares one of two layouts:
- MainLayout — includes a nav header, content body, and copyright footer. Building it is expensive (reads config, sets defaults).
- BlankLayout — an empty layout with no header or footer, used for login/error pages.
Instead of re-building MainLayout for every new page, we keep a single cached instance (MainLayoutIns) and clone it each time NewPage() is called. Each Page gets its own independent copy — mutations to one page's layout never bleed into another.

III. Implementation
Layout struct
// layout.go
package prototype
type Layout struct {
Header string
Body string
Footer string
}Singleton prototypes
// layout.go
package prototype
const (
BLANK_LAYOUT = iota + 1
MAIN_LAYOUT
)
var BlankLayoutIns = Layout{
Header: "",
Body: "",
Footer: "",
}
var MainLayoutIns = Layout{
Header: "<nav>Main navigation menu</nav>",
Body: "",
Footer: "<footer>Copyright 2026</footer>",
}cloneLayout — the copy function
// layout.go
package prototype
func cloneLayout(l int) Layout {
switch l {
case BLANK_LAYOUT:
copy := BlankLayoutIns
return copy
case MAIN_LAYOUT:
copy := MainLayoutIns
return copy
default:
return Layout{}
}
}In Go, assigning a struct to a new variable produces a value copy — all fields are duplicated. This is the prototype clone in action.
Page struct and NewPage constructor
// page.go
package prototype
type Page struct {
Layout
URI string
}
func NewPage(uri string, layoutType int) Page {
return Page{
Layout: cloneLayout(layoutType),
URI: uri,
}
}IV. Explain the Example Above
Let's trace the execution step by step:
Step 1 — Create a page with MainLayout:
homePage := NewPage("/home", MAIN_LAYOUT)
// cloneLayout(MAIN_LAYOUT) copies MainLayoutIns into a new Layout value
// homePage.Header = "<nav>Main navigation menu</nav>"
// homePage.Footer = "<footer>Copyright 2026</footer>"Step 2 — Create another page with the same layout:
profilePage := NewPage("/profile", MAIN_LAYOUT)Step 3 — Mutate one page's layout without affecting the other:
homePage.Header = "<nav>Home-specific banner</nav>"
fmt.Println(homePage.Header) // "<nav>Home-specific banner</nav>"
fmt.Println(profilePage.Header) // "<nav>Main navigation menu</nav>" — unchanged
fmt.Println(MainLayoutIns.Header) // "<nav>Main navigation menu</nav>" — prototype untouchedOutput:
/home header: <nav>Home-specific banner</nav>
/profile header: <nav>Main navigation menu</nav>This demonstrates the key property: each call to NewPage() produces a fully independent copy. The MainLayoutIns singleton is never modified.
BlankLayout for login page:
loginPage := NewPage("/login", BLANK_LAYOUT)
fmt.Println(loginPage.Header) // ""
fmt.Println(loginPage.Footer) // ""V. Conclusion
The Prototype pattern is elegant when object creation cost is the bottleneck. In Go, the pattern maps naturally to struct assignment — there's no need for a Clone() interface unless you're working with pointer fields that require deep copying.
Key takeaways:
- Go struct assignment is a shallow copy — sufficient when all fields are value types (strings, ints, bools)
- If your struct contains slices, maps, or pointers, you need an explicit deep copy function
- Singleton prototypes + clone = cache the expensive initialization once, reuse forever
Compared to other creational patterns:
- Builder — constructs objects step by step from scratch
- Factory Method — delegates creation to a function/subclass
- Prototype — copies an existing, pre-initialized instance
As with all design patterns, no pattern is universally superior. Prototype is most valuable when initialization cost dominates and your objects share a common starting state.
VI. References
- Book: Go Design Patterns by Mario Castro Contreras
