Introduction
The prototype-pattern is a creational design pattern that allows us to create new objects by cloning existing ones, i.e. the existing objects function as a kind of template. This can save time in some cases when for example an object creation involves some heavy calculation or things like network traffic or database queries.
So what does it look like? Well, it is actually quite simple:

A short explanation:
- The main part is the Prototype interface. This defines a Clone() method which returns a Prototype. We will see in the implementation why this is important.
- There are some concrete classes which implement the Clone() method. In this example there are two, but this could of course be any number.
- Finally there is the Client, the class which needs the concrete classes.
Implementation in Go
Open your terminal or commandline in an empty directory and type:
mkdir go_prototype
cd go_prototype
go mod init github.com/proxy_pattern
Now open your favourite IDE in this directory, and add a main.go file. In that file first type:
package main
import "fmt"
Now we can define the Prototype interface:
type Prototype[T any] interface {
Clone() T
}
As you can see this is a generic interface.. We need to do this, to make the code more typesafe as we shall see later on.
To demonstrated deep copying later on, let’s make an Address struct:
type Address struct {
Street string
City string
}
In this example we define and implement a ConcretePrototype:
type ConcretePrototype struct {
name string
age int
address *Address
tags []string
}
func (c *ConcretePrototype) Clone() *ConcretePrototype {
newAddress := &Address{
Street: c.address.Street,
City: c.address.City,
}
newTags := make([]string, len(c.tags))
copy(newTags, c.tags)
return &ConcretePrototype{
name: c.name,
age: c.age,
address: newAddress,
tags: newTags,
}
}
Some explanation:
- The
ConcretePrototypehas several fields: name, age, tags which is a string slice, and a field with typeAddress - The
Clone()method does the following:- create a new address using the existing values.
- Create a copy of the tags slice.
- Finally, create a new
ConcretePrototypestruct and return it.
Time to test
Now let the theory become practice:
func main() {
original := &ConcretePrototype{
name: "Original",
age: 25,
address: &Address{
Street: "123 Main St",
City: "Springfield",
},
tags: []string{"employee", "developer"},
}
// No type assertion needed - Clone() returns *ConcretePrototype
clone1 := original.Clone()
clone2 := original.Clone()
// Modify clones to demonstrate independence
clone1.name = "Clone 1"
clone1.address.City = "New York"
clone1.tags[0] = "manager"
clone2.name = "Clone 2"
clone2.address.Street = "456 Oak Ave"
fmt.Printf("Original: Name=%s, Age=%d, Address=%+v, Tags=%v\n",
original.name, original.age, *original.address, original.tags)
fmt.Printf("Clone 1: Name=%s, Age=%d, Address=%+v, Tags=%v\n",
clone1.name, clone1.age, *clone1.address, clone1.tags)
fmt.Printf("Clone 2: Name=%s, Age=%d, Address=%+v, Tags=%v\n",
clone2.name, clone2.age, *clone2.address, clone2.tags)
}
A short description
- We construct a ConcretePrototype
- We clone this prototype and change some fields.
- We repeat this.
- By printing out we can see that we have cloned correctly and that each clone is independent of one another.
Conclusion
The Prototype Pattern offers a clean and efficient way to create new objects by cloning existing ones, avoiding the cost of complex instantiation logic. As shown in the Go implementation, it helps you make deep copies of objects safely and type-safely, without having to rebuild them from scratch. This pattern shines in situations where object creation is expensive or involves shared configurations. In short, the Prototype Pattern helps keep your code flexible, efficient, and easier to maintain when dealing with object duplication.




