Design Pattern Series - Prototype (Golang)

Golang Sep 9, 2025

Previous post in series Builder Pattern

The Prototype Design Pattern is a creational design pattern that allows new objects to be created by copying an existing object, called a prototype, instead of creating them from scratch. This pattern is especially useful when creating an object that is expensive or complex, and you want to reuse the structure of an existing object.

What is Prototype Design Pattern?

The Prototype Pattern involves creating a prototype object and cloning it to create new objects. Instead of using a constructor or a factory, this pattern relies on a cloning mechanism to reproduce the state of the prototype. It is useful in cases where:

  • Creating new objects is resource-intensive (e.g. requires database queries or complex initialization).
  • Need to create objects with small differences from the original object.
  • Want to avoid using subclasses to create objects.

In Golang, which does not have traditional inheritance, the Prototype Pattern can be implemented using a struct and an interface to achieve similar functionality.

Main components of Prototype Pattern

  1. Prototype Interface: Defines a method to clone objects.
  2. Concrete Prototype: Implements the clone method and stores the state to be cloned.
  3. Client: Uses the prototype to create new objects by cloning.

Implement the Prototype Pattern in Golang

We will create a system for managing document types (e.g., resumes) that can be cloned and customized to demonstrate this pattern:

=> Implement the Prototype Pattern with a resume document.

package main

import (
	"fmt"
)

// Prototype interface
type Document interface {
	Clone() Document
	Display()
}

// Concrete Prototype: Resume
type Resume struct {
	Name      string
	Skills    []string
	Experience string
}

// Clone method for Resume
func (r *Resume) Clone() Document {
	// Deep copy to avoid slice share
	skillsCopy := make([]string, len(r.Skills))
	copy(skillsCopy, r.Skills)

	return &Resume{
		Name:      r.Name,
		Skills:    skillsCopy,
		Experience: r.Experience,
	}
}

// Display method for Resume
func (r *Resume) Display() {
	fmt.Printf("Resume: %s\nSkills: %v\nExperience: %s\n\n", r.Name, r.Skills, r.Experience)
}

func main() {
	// Create a prototype resume
	prototypeResume := &Resume{
		Name:      "John Doe",
		Skills:    []string{"Go", "Python", "SQL"},
		Experience: "5 years of SE",
	}

	// Clone prototype to create new resume
	resume1 := prototypeResume.Clone()
	resume2 := prototypeResume.Clone()

	// Edit created resume
	resume1.(*Resume).Name = "Alice Smith"
	resume1.(*Resume).Skills = append(resume1.(*Resume).Skills, "JavaScript")

	resume2.(*Resume).Name = "Bob Johnson"
	resume2.(*Resume).Experience = "3 years of Backend Developer"

	// Render
	fmt.Println("Prototype Resume:")
	prototypeResume.Display()

	fmt.Println("Cloned Resume 1:")
	resume1.Display()

	fmt.Println("Cloned Resume 2:")
	resume2.Display()
}

Explain:

  1. Prototype Interface:
    • Interface Document define method Clone() return a new Document and method Display() to display the state of the object.
  2. Concrete Prototype (Resume):
    • Struct Resume implement interface Document.
    • Method Clone() create a copy (deep copy) of the resume, make sure that the slices are Skills not shared between prototype and copy.
    • Method Display() print details of the resume.
  3. Client (main):
    • A prototype resume is created with default values.
    • Two new resumes are created by cloning the prototype.
    • Each cloned resume can be edited independently without affecting the prototype or other copies.

Output

Prototype Resume:
Resume: John Doe
Skills: [Go Python SQL]
Experience: 5 years of SE

Cloned Resume 1:
Resume: Alice Smith
Skills: [Go Python SQL JavaScript]
Experience: 5 years of SE

Cloned Resume 2:
Resume: Bob Johnson
Skills: [Go Python SQL]
Experience: 3 years of Backend Developer

This shows that the copies are independent of the prototype and of each other.

Benefit of the Prototype Pattern

  • Efficiency: Reduce the cost of creating complex objects by reusing existing objects.
  • Flexible: Allows dynamic creation of objects with different configurations.
  • Simplified object creation: Avoids complex constructors or factories, especially in languages ​​like Go that do not have class inheritance.
  • Decoupling: Clients do not need to know the details of object creation.

When to use the Prototype Pattern

  • When object instantiation is resource-intensive (e.g., loading data from a database).
  • When multiple objects with similar properties but slight differences are needed.
  • When you want to avoid using subclasses to create object variants.
  • When the system needs to be independent of how objects are created.

Note in Golang

  • Deep Copy vs. Shallow Copy: In Go, it is necessary to ensure deep copy for fields like slices or maps to avoid unintended reference sharing. The above example explicitly copies the Skills slice.
  • Interface: Go's interfaces are well suited for defining the Clone() method, making this pattern flexible and reusable.
  • Concurrency: If using in a concurrent environment, make sure the prototype is thread-safe or immutable to avoid race conditions.

Conclude

Prototype Design Pattern is a powerful tool for efficient object creation in Golang. By leveraging interfaces and careful cloning, you can implement this pattern to simplify object creation and improve performance.

The above example demonstrates cloning a resume, but this pattern can be applied to many different areas, such as configuration management, game development, or any situation where object cloning is needed.

Thanks for reading!

Tags