Multiton Design Pattern in Golang Two Examples with Unit Tests
In this article, I will explain the multiton design pattern’s concept, objectives, pros and cons, scenarios and how to implement, and provide two instances and unit tests.

Concept
The Multiton design pattern is a creational pattern that ensures a struct has only a limited number of instances, representing each instance by one of several possible keys. The key is used as an identifier to obtain a single instance from a registry of instances that are managed by the multiton.
The Multiton design pattern is similar to the Singleton pattern but with a twist. The Singleton pattern ensures that a struct has only one instance. In contrast, the Multiton pattern ensures that a struct has a limited number of instances, each with its own unique identifier. The Multiton pattern is helpful in situations where multiple instances of a struct are required, but each instance must have a unique identity.
Objectives
The objectives of the Multiton pattern are:
- Manage multiple instances of a struct, each with a unique identifier.
- Provide a global point of access to instances.
- Ensure that only one instance exists for each key.
Pros and Cons
Advantages of using the Multiton pattern:
- Enforces a single instance of a struct per key.
- Provides a centralized point of control for the instances.
- Supports efficient instance retrieval using the key.
- It avoids the need to create multiple instances of a struct in different parts of the code.
Disadvantages of using the Multiton pattern:
- Increases complexity compared to Singleton.
- This may lead to increased memory consumption for a large number of instances.
- It can be prone to concurrency issues if not implemented correctly.
Scenarios
Here are some real-world scenarios where you could use the Multiton pattern in Golang:
1. Database Connection Pool
When managing multiple connections to different databases, you can use the Multiton pattern to create a pool of connections for each database. Each pool is keyed by the database connection string or some other identifier, allowing efficient reuse of connections and preventing excessive creation of new connections.
2. Resource Management
When managing access to shared hardware resources, like printers, scanners, or IoT devices, the Multiton pattern can be used to create and manage separate instances for each resource. Instances are keyed by a unique identifier, such as a device ID, allowing for efficient allocation and controlled access to shared resources.
3. Logger Instances
When creating a logging system that handles logs from multiple modules or components, you can use the Multiton pattern to create separate logger instances for each module. Each logger instance is keyed by a unique identifier, such as the module name, ensuring that log messages are correctly segregated and can be filtered or processed independently.
4. Load Balancer
If your application relies on a set of backend services, you can use the Multiton pattern to implement a load balancer that maintains multiple instances of the backend services. Each instance can be identified by a unique key, and the load balancer can distribute the workload among the instances based on their capacity and availability.
5. Cache
The Multiton pattern can be used to implement caching mechanisms where you store frequently accessed data in memory for faster retrieval. Each cached object can be identified by a unique key, allowing you to efficiently access the data without the need for redundant queries or computations.

How to implement
- Define the Multiton struct with a private constructor and a private map to store instances.
- Create a mutex for thread-safe access to instances.
- Implement a function to get an instance of the Multiton struct based on a key.
- Use the
GetInstance()function to retrieve instances of the Multiton struct.
The following image is how to implement the multiton pattern diagram. Perhaps you think about Golang does not have a class. Correct, Golang does not have classes in the traditional sense like some other object-oriented programming languages such as Java or C++. Instead, Go has types with methods, and it uses interfaces to achieve polymorphism. I use the term “class” as a general term to represent a type that has methods.

First instance
In the following instance, I will implement the ice cream instance. I will give a simple example first. The below code is the icecream.go file content. The parameter fruit in GetIceCream() method, is the key identifier for the multiton pattern. Here the GetIceCream() function is as the GetInstance().
package icecream
import (
"fmt"
"sync"
)
// Step 1: Define the Multiton struct with a private constructor
// and a private map to store instances.
type Multiton struct {
Fruit string
Favour string
}
var (
fruitMap = make(map[string]*Multiton)
mutex = &sync.Mutex{} // Step 2: Create a mutex for thread-safe access to instances.
)
// Step 3: Implement a function to get an instance of the Multiton struct based on a key.
func GetIceCream(fruit string) *Multiton {
mutex.Lock()
defer mutex.Unlock()
if fruitMap[fruit] == nil {
fruitMap[fruit] = &Multiton{
Fruit: fruit,
Favour: fmt.Sprintf("Favour is %s", fruit),
}
}
return fruitMap[fruit]
}This implementation ensures that only one instance of the Multiton struct exists for each unique key and provides a global point of access to those instances.
The following code is the icecream_test.go file content.
package icecream
import (
"testing"
"github.com/go-playground/assert/v2"
)
func TestGetIceCream(t *testing.T) {
// Step 4: Use the GetIceCream() as the GetInstance() function
// to retrieve instances of the Multiton struct.
appleCream := GetIceCream("apple")
berryCream := GetIceCream("strawberry")
watermelonCream := GetIceCream("watermelon")
assert.Equal(t, "Favour is apple", appleCream.Favour)
assert.Equal(t, "Favour is strawberry", berryCream.Favour)
assert.Equal(t, "Favour is watermelon", watermelonCream.Favour)
}After these codes are finished, let’s run the unit tests. The tests result from the screenshot is below.

Second instance
In the below instance, I will implement the database config instance. The following code is the db.go file content. It is a little bit more complex than the first example. DBConfig represents a database configuration. ConfigManager represents a database configuration manager. ConfManager is a singleton instance of ConfigManager. GetConfig returns a database configuration for a given key. SetConfig sets a database configuration for a given key.
package dbconn
import (
"fmt"
"sync"
)
type DBConfig struct {
Username string
Password string
Host string
Port int
DBName string
}
type ConfigManager struct {
configs map[string]*DBConfig
once sync.Once
}
var ConfManager ConfigManager
func (c *ConfigManager) GetConfig(key string) (*DBConfig, error) {
c.once.Do(func() {
c.configs = make(map[string]*DBConfig)
})
config, ok := c.configs[key]
if !ok {
return nil, fmt.Errorf("config for key '%s' not found", key)
}
return config, nil
}
func (c *ConfigManager) SetConfig(key string, config *DBConfig) {
c.once.Do(func() {
c.configs = make(map[string]*DBConfig)
})
c.configs[key] = config
}In this example, we have a database configuration manager represented by the ConfigManager struct. The manager holds a map of database configurations, with each configuration identified by a unique key. The sync.Once type is used to ensure that the map of configurations is only created once, even if multiple goroutines attempt to access the map concurrently. The Instance variable is a singleton instance of ConfigManager, and the GetConfig and SetConfig methods allow for retrieving and setting database configurations in the map, respectively. Please notice in the first example, I use the sync.Mutex, in the second example, I use the sync.Once. Usages of them are different.
The below code is the db_test.go file content.
package dbconn
import (
"fmt"
"testing"
"github.com/go-playground/assert/v2"
)
func TestConfig(t *testing.T) {
confTest := &DBConfig{
Username: "user1",
Password: "pass1",
Host: "localhost",
Port: 5432,
DBName: "db1",
}
ConfManager.SetConfig("config test", confTest)
configDev := &DBConfig{
Username: "user2",
Password: "pass2",
Host: "localhost",
Port: 5433,
DBName: "db2",
}
ConfManager.SetConfig("config dev", configDev)
config, err := ConfManager.GetConfig("config test")
if err != nil {
fmt.Println(err)
return
}
assert.Equal(t, confTest.Username, config.Username)
assert.Equal(t, confTest.Port, config.Port)
config, err = ConfManager.GetConfig("config dev")
if err != nil {
fmt.Println(err)
return
}
assert.Equal(t, configDev.Username, config.Username)
assert.Equal(t, configDev.Port, config.Port)
}After these codes are finished, let’s run the unit tests. The tests result from the screenshot is below.

Conclusion
In conclusion, the Multiton pattern aims to solve the problem of managing multiple instances of a struct while ensuring that each instance is unique and can be easily accessed using a key. Implementing a multiton ensures that instances are created only when needed and reused whenever possible, leading to a more efficient and maintainable codebase.
Go back to Creational Design Patterns click here.
To View Structural Design Patterns in Golang, please click here.
To View Behavioural Design Patterns in Golang, please click here.
To View Concurrency Design Patterns in Golang, please click here.





