Multiton
The Multiton pattern is a variation of the Singleton pattern. While a Singleton restricts the instantiation of a class to only one instance, a Multiton allows a limited number of instances. This is useful when multiple instances are needed to represent different, but related, states or configurations without allowing unlimited creation. The Multiton controls the number of instances created and manages access to them.
It differs from a simple factory in that the Multiton remembers the created instances and provides access to them, whereas a factory simply creates and returns new instances each time. This pattern ensures that only a predefined number of instances of a class exist throughout the application lifecycle, improving resource management and potentially simplifying coordination between these instances.
Usage
The Multiton pattern is useful in scenarios where:
- Limited Resources: You need to control the number of instances of a resource-intensive class to prevent performance issues or resource exhaustion. For example, database connection pools.
- Configuration Management: You need to manage a small set of configuration objects, each representing a different environment or setting.
- Load Balancing: You want to distribute work across a fixed number of worker instances.
- Regional Servers: Managing a limited number of server instances in different geographical regions.
Examples
-
Database Connection Pool: Many database libraries utilize a Multiton-like approach to manage a pool of database connections. Instead of creating a new connection for every request, the pool maintains a limited number of connections, reusing them to improve performance and reduce overhead. Libraries like HikariCP or Apache DBCP implement this concept.
-
Log Managers with Multiple Log Files: A logging framework might use a Multiton to manage a fixed number of log files. Each instance of the Multiton represents a different log file (e.g., one for errors, one for warnings, one for information). This allows for organized logging without the overhead of creating a new file handler for every log message. Log4j2 and similar frameworks can be configured to behave in this manner.
Specimens
15 implementationsThe Multiton pattern ensures that only a limited number of instances of a class can exist. It’s a relaxed version of the Singleton pattern. This is achieved by maintaining a static list or map to store the instances and controlling the instance creation process. The code uses a static, private map to hold the instances, keyed by a string identifier. getInstance() checks if an instance with the given key exists; if not, it creates one and adds it to the map. This implementation is idiomatic Dart as it leverages Dart’s strong typing, private members (_), and the use of maps for flexible instance management, avoiding unnecessary class hierarchies.
class Multiton {
static final Map<String, Multiton> _instances = <String, Multiton>{};
final String _key;
// Private constructor to prevent direct instantiation
Multiton._(this._key);
// Public factory method to get an instance
factory Multiton(String key) {
if (_instances[key] == null) {
_instances[key] = Multiton._(key);
}
return _instances[key];
}
// Example method
void doSomething() {
print('Multiton instance with key $_key is doing something.');
}
// Optional: Method to get the number of instances
static int instanceCount() {
return _instances.length;
}
}
void main() {
final instance1 = Multiton('A');
final instance2 = Multiton('A'); // Returns the same instance as instance1
final instance3 = Multiton('B');
final instance4 = Multiton('C');
instance1.doSomething();
instance3.doSomething();
print('Number of instances: ${Multiton.instanceCount()}'); // Output: 3
}
The Multiton pattern ensures that only a fixed number of instances of a class can be created. It’s a relaxed version of the Singleton pattern. This implementation uses a lazy val collection to store the instances, creating them on demand up to the specified maximum. The apply method provides access to these instances, cycling through them if the requested index is out of bounds. This approach leverages Scala’s immutability and lazy initialization for thread safety and efficiency, fitting well with Scala’s functional style while still utilizing object-oriented principles.
object Multiton {
private val instances = (1 to 3).map(i => new MultitonInstance(i)).toList
private var currentIndex = 0
def apply(index: Int = 0): MultitonInstance = {
val actualIndex = currentIndex % instances.length
currentIndex += 1
instances(actualIndex)
}
}
class MultitonInstance(val id: Int) {
def doSomething(): String = s"Multiton instance $id is doing something."
}
// Example Usage
object Main {
def main(args: Array[String]): Unit = {
val instance1 = Multiton()
val instance2 = Multiton()
val instance3 = Multiton()
val instance4 = Multiton() // Cycles back to the first instance
println(instance1.doSomething())
println(instance2.doSomething())
println(instance3.doSomething())
println(instance4.doSomething())
}
}
The Multiton pattern ensures a restricted number of instances of a class are created, and provides a global access point to these instances. It’s a variation of the Singleton pattern, allowing for more than one instance but still controlling instantiation. This implementation uses a static array to store the instances and a static method to retrieve them, creating them on demand up to the defined limit. PHP’s static properties and methods make this a natural fit, avoiding the need for complex dependency injection or service locators for controlled instantiation.
<?php
class Multiton
{
private static $instances = [];
private static $maxInstances = 3;
private $id;
private function __construct(int $id)
{
$this->id = $id;
}
public static function getInstance(int $id): self
{
if (!isset(self::$instances[$id])) {
if (count(self::$instances) < self::$maxInstances) {
self::$instances[$id] = new self($id);
} else {
throw new \Exception("Maximum number of instances reached.");
}
}
return self::$instances[$id];
}
public function getId(): int
{
return $this->id;
}
private function __clone() {} // Prevent cloning
private function __wakeup() {} // Prevent unserialization
}
// Example Usage:
try {
$instance1 = Multiton::getInstance(1);
$instance2 = Multiton::getInstance(2);
$instance3 = Multiton::getInstance(3);
echo "Instance 1 ID: " . $instance1->getId() . PHP_EOL;
echo "Instance 2 ID: " . $instance2->getId() . PHP_EOL;
echo "Instance 3 ID: " . $instance3->getId() . PHP_EOL;
// Attempting to create a fourth instance will throw an exception
$instance4 = Multiton::getInstance(4);
} catch (\Exception $e) {
echo "Error: " . $e->getMessage() . PHP_EOL;
}
?>
The Multiton pattern ensures that only a limited number of instances of a class can exist. Unlike a Singleton which restricts to just one instance, a Multiton allows a specified quantity. This example uses a class variable @instances to store the created instances and a class method instance to manage their creation, limiting the count to a predefined MAX_INSTANCES. The implementation is idiomatic Ruby due to its use of class variables for shared state, and class methods for controlled instance access, leveraging Ruby’s dynamic nature and meta-programming capabilities. It avoids explicit locking mechanisms, relying on Ruby’s inherent thread safety for simple instance creation.
# multiton.rb
class Multiton
MAX_INSTANCES = 3
@@instances = []
private_class_method :new
def self.instance(key)
instance = @@instances.find { |i| i.key == key }
unless instance
if @@instances.length < MAX_INSTANCES
instance = new(key)
@@instances << instance
else
raise "Maximum number of instances reached (#{MAX_INSTANCES})"
end
end
instance
end
def initialize(key)
@key = key
end
attr_reader :key
end
# Example Usage
begin
instance1 = Multiton.instance("A")
instance2 = Multiton.instance("B")
instance3 = Multiton.instance("C")
puts "Instance 1 key: #{instance1.key}"
puts "Instance 2 key: #{instance2.key}"
puts "Instance 3 key: #{instance3.key}"
instance4 = Multiton.instance("D") # Raises an exception
rescue => e
puts "Error: #{e.message}"
end
The Multiton pattern ensures a restricted number of instances of a class. Unlike a Singleton which allows only one instance, a Multiton allows a small, predefined set. This is useful when you need a few, globally accessible instances representing distinct, but related, states or roles.
This Swift implementation uses a static array to hold the instances and a static method to access them based on an index or identifier. It leverages Swift’s strong typing and guard let to ensure safe access to existing instances or creation of new ones within the defined limit. This approach is considered idiomatic in Swift because it relies on static properties for controlled access and utilizes optionals for managing potential nil values during initialization, aligning with Swift’s safety-focused design.
class Multiton {
static private var instances: [Int: Multiton] = [:]
static private let maxInstances = 3
static func getInstance(forKey key: Int) -> Multiton {
if instances[key] == nil && key < maxInstances {
instances[key] = Multiton()
}
return instances[key]!
}
private init() {
// Private initializer to prevent direct instantiation
}
func doSomething(withKey key: Int) {
print("Multiton instance with key \(key) is doing something.")
}
}
// Usage:
let instance1 = Multiton.getInstance(forKey: 0)
let instance2 = Multiton.getInstance(forKey: 1)
let instance3 = Multiton.getInstance(forKey: 2)
instance1.doSomething(withKey: 0)
instance2.doSomething(withKey: 1)
instance3.doSomething(withKey: 2)
// Attempting to get an instance with an invalid key:
let instance4 = Multiton.getInstance(forKey: 3) // Will return instance3 because maxInstances is 3
The Multiton pattern ensures that only a fixed number of instances of a class can exist. It’s a variation of the Singleton pattern, where rather than a single instance, you allow a limited, pre-defined number. This can be useful for managing resources like database connections or limited-license software access. This Kotlin implementation uses a companion object to hold a fixed-size list of instances, created only when accessed for the first time. It leverages mutableListOf for the instance storage and synchronization is handled implicitly by Kotlin’s thread-safety features within the companion object initialization. This approach is concise and idiomatic for Kotlin, avoiding explicit locking mechanisms.
class Multiton(val id: Int) {
companion object {
private val maxInstances = 3
private val instances = mutableListOf<Multiton>()
private var instanceCounter = 0
fun getInstance(): Multiton {
return synchronized(this) {
if (instances.size < maxInstances) {
val newInstance = Multiton(instanceCounter++)
instances.add(newInstance)
newInstance
} else {
// Return an existing instance randomly
instances.random()
}
}
}
}
}
fun main() {
val instance1 = Multiton.getInstance()
val instance2 = Multiton.getInstance()
val instance3 = Multiton.getInstance()
val instance4 = Multiton.getInstance() // Will return one of the existing instances
println("Instance 1 ID: ${instance1.id}")
println("Instance 2 ID: ${instance2.id}")
println("Instance 3 ID: ${instance3.id}")
println("Instance 4 ID: ${instance4.id}")
println("Number of instances created: ${Multiton.instances.size}")
}
The Multiton pattern ensures that only a specific number of instances of a class exist. It’s a relaxed version of the Singleton pattern, allowing for a limited concurrency or distribution of instances. This Rust implementation uses a static mutable vector to store the instances and a Mutex to provide thread-safe access. The instance() method retrieves an instance based on a key; if one doesn’t exist, it creates it, respecting the maximum allowed count. Rust’s ownership and borrowing rules, combined with Mutex, naturally enforce safety when dealing with shared mutable state, making this a clean and effective approach.
use std::sync::{Mutex, Once};
struct MultitonInstance {
id: u32,
}
impl MultitonInstance {
fn new(id: u32) -> Self {
MultitonInstance { id }
}
fn print_id(&self) {
println!("Instance ID: {}", self.id);
}
}
struct Multiton {
instances: Mutex<Vec<MultitonInstance>>,
max_instances: u32,
next_id: Mutex<u32>,
init: Once,
}
impl Multiton {
fn new(max_instances: u32) -> Self {
Multiton {
instances: Mutex::new(Vec::new()),
max_instances,
next_id: Mutex::new(0),
init: Once::new(),
}
}
fn instance(&self, id: u32) -> Result<MultitonInstance, String> {
self.init.call_once(|| {
// Initialization logic can go here if needed.
});
let mut instances = self.instances.lock().unwrap();
let mut next_id = self.next_id.lock().unwrap();
if instances.len() < self.max_instances as usize {
if *next_id < self.max_instances {
let new_instance = MultitonInstance::new(*next_id);
instances.push(new_instance);
*next_id += 1;
Ok(instances.iter().find(|i| i.id == *next_id -1).unwrap().clone())
} else {
Err("Maximum instances reached".to_string())
}
} else {
if instances.iter().any(|i| i.id == id) {
Ok(instances.iter().find(|i| i.id == id).unwrap().clone())
} else {
Err("Maximum instances reached".to_string())
}
}
}
}
fn main() {
let multiton = Multiton::new(3);
match multiton.instance(0) {
Ok(instance) => instance.print_id(),
Err(e) => println!("{}", e),
}
match multiton.instance(1) {
Ok(instance) => instance.print_id(),
Err(e) => println!("{}", e),
}
match multiton.instance(2) {
Ok(instance) => instance.print_id(),
Err(e) => println!("{}", e),
}
match multiton.instance(3) {
Ok(instance) => instance.print_id(),
Err(e) => println!("{}", e),
}
match multiton.instance(0) {
Ok(instance) => instance.print_id(),
Err(e) => println!("{}", e),
}
}
The Multiton pattern ensures that only a limited number of instances of a class are created. It’s a variation of the Singleton pattern, allowing for a small, predefined count of instances instead of just one. This is useful when a fixed number of resources or managers are needed and creating more would be detrimental. In Go, this is implemented using a map to store instances, a mutex to ensure thread safety during instance creation, and a function to retrieve an instance, creating it if it doesn’t already exist within the allowed count. Go’s built-in concurrency features are leveraged for safe access.
package main
import (
"fmt"
"sync"
)
type multiton struct {
id int
}
var (
instances = make(map[int]*multiton)
instanceCount = 0
mu sync.Mutex
maxInstances = 3
)
func GetInstance(id int) *multiton {
mu.Lock()
defer mu.Unlock()
if _, ok := instances[id]; !ok && instanceCount < maxInstances {
instances[id] = &multiton{id: id}
instanceCount++
}
return instances[id]
}
func main() {
instance1 := GetInstance(1)
instance2 := GetInstance(2)
instance3 := GetInstance(3)
instance4 := GetInstance(4) // Will return nil because maxInstances is 3
fmt.Printf("Instance 1 ID: %d\n", instance1.id)
fmt.Printf("Instance 2 ID: %d\n", instance2.id)
fmt.Printf("Instance 3 ID: %d\n", instance3.id)
if instance4 == nil {
fmt.Println("Instance 4 is nil, maximum instances reached.")
}
fmt.Printf("Total instances created: %d\n", instanceCount)
}
The Multiton pattern ensures a limited number of instances of a class are created, and provides a global access point to each of those instances. It’s a relaxed version of the Singleton pattern, allowing for more concurrency or logical separation. This C implementation uses a static array to hold the instances, indexed by an enum. A check within the instance creation function prevents exceeding the defined number of multiton instances. It’s a relatively straightforward approach in C, relying on static storage and a simple instantiation control mechanism. Using an enum for the instances is a common C practice for creating named constants.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// Define the maximum number of instances
#define MAX_INSTANCES 3
// Enum to represent the different instances
typedef enum {
INSTANCE_ONE,
INSTANCE_TWO,
INSTANCE_THREE
} InstanceType;
// Structure for the Multiton class
typedef struct {
int data;
bool is_initialized;
} Multiton;
// Static array to hold the instances
static Multiton instances[MAX_INSTANCES];
// Function to get an instance of the Multiton
Multiton* get_instance(InstanceType type) {
if (type >= MAX_INSTANCES) {
fprintf(stderr, "Invalid instance type!\n");
return NULL;
}
if (!instances[type].is_initialized) {
instances[type].data = type + 1; // Initialize with some data
instances[type].is_initialized = true;
}
return &instances[type];
}
int main() {
Multiton* instance1 = get_instance(INSTANCE_ONE);
Multiton* instance2 = get_instance(INSTANCE_TWO);
Multiton* instance3 = get_instance(INSTANCE_THREE);
if (instance1) {
printf("Instance 1 data: %d\n", instance1->data);
}
if (instance2) {
printf("Instance 2 data: %d\n", instance2->data);
}
if (instance3) {
printf("Instance 3 data: %d\n", instance3->data);
}
//Demonstrate that subsequent calls return the same instance
Multiton* instance1_again = get_instance(INSTANCE_ONE);
if (instance1_again == instance1) {
printf("Instance 1 re-obtained. Pointers are equal.\n");
}
return 0;
}
The Multiton pattern ensures that only a specific number of instances of a class exist. It’s a relaxed version of the Singleton pattern. This is achieved by maintaining a static collection (like a vector) of instances and controlling access to them. The example below implements a Multiton for a Logger class, allowing for a maximum of three logger instances, each with a different severity level (Debug, Info, Error). The getInstance() method returns the first available instance of a requested severity, or creates a new one if space permits. C++’s static member variables and the use of a std::vector to store the instances are idiomatic approaches to implementing this resource-control pattern.
#include <iostream>
#include <vector>
#include <mutex>
enum class Severity {
Debug,
Info,
Error
};
class Logger {
public:
~Logger() {
std::cout << "Logger " << severity_ << " destroyed." << std::endl;
}
static Logger& getInstance(Severity severity) {
std::lock_guard<std::mutex> lock(mutex_);
for (auto& instance : instances_) {
if (instance.severity_ == severity && !instance.is_used_) {
instance.is_used_ = true;
return instance;
}
}
if (instances_.size() < max_instances_) {
Logger new_instance(severity);
instances_.push_back(new_instance);
return instances_.back();
}
throw std::runtime_error("Maximum number of loggers reached for severity " + std::to_string(static_cast<int>(severity)));
}
void log(const std::string& message) {
std::cout << "[" << severityToString(severity_) << "] " << message << std::endl;
}
private:
Logger(Severity severity) : severity_(severity), is_used_(true) {}
Severity severity_;
bool is_used_;
std::string severityToString(Severity sev) const{
switch(sev){
case Severity::Debug: return "DEBUG";
case Severity::Info: return "INFO";
case Severity::Error: return "ERROR";
default: return "UNKNOWN";
}
}
static std::vector<Logger> instances_;
static const int max_instances_ = 3;
static std::mutex mutex_;
};
std::vector<Logger> Logger::instances_;
const int Logger::max_instances_;
std::mutex Logger::mutex_;
int main() {
try {
Logger& debugLogger = Logger::getInstance(Severity::Debug);
Logger& infoLogger = Logger::getInstance(Severity::Info);
Logger& errorLogger = Logger::getInstance(Severity::Error);
Logger& anotherErrorLogger = Logger::getInstance(Severity::Error); //Uses existing error logger
debugLogger.log("Debugging message.");
infoLogger.log("Information message.");
errorLogger.log("Error message.");
anotherErrorLogger.log("Another error message from existing instance.");
// The following would throw an exception because the maximum number of instances is reached.
// Logger& yetAnotherLogger = Logger::getInstance(Severity::Debug);
} catch (const std::runtime_error& e) {
std::cerr << "Exception: " << e.what() << std::endl;
}
return 0;
}
The Multiton pattern ensures that only a fixed number of instances of a class exist. It’s a variation of the Singleton pattern, but instead of one instance, it allows for a small, predefined set. This is useful for managing resources where a limited pool is appropriate — think database connections, printer spoolers, or a fixed configuration set.
The C# implementation uses a private static readonly list to hold the instances. A static factory method creates instances only if the count is below the specified maximum, and retrieves existing instances after that. The IsRegistered() method confirms instance existence. This approach leverages C#’s static initialization and list capabilities to achieve the Multiton behavior. Using a list makes the code easily extensible for managing more than one instance if needed.
using System;
using System.Collections.Generic;
public class Multiton
{
private static readonly List<Multiton> _instances = new(3); // Maximum 3 instances
private readonly int _id;
private Multiton(int id)
{
_id = id;
Console.WriteLine($"Multiton instance {_id} created.");
}
public static Multiton GetInstance()
{
if (_instances.Count < 3)
{
lock (typeof(Multiton))
{
if (_instances.Count < 3) //Double check locking for thread safety
{
_instances.Add(new Multiton(_instances.Count + 1));
return _instances[^1]; // Return the last element
}
}
}
// Return an existing instance (round-robin)
return _instances[new Random().Next(_instances.Count)];
}
public int GetId()
{
return _id;
}
private static bool IsRegistered(Multiton instance)
{
return _instances.Contains(instance);
}
}
public class Example
{
public static void Main(string[] args)
{
Multiton instance1 = Multiton.GetInstance();
Multiton instance2 = Multiton.GetInstance();
Multiton instance3 = Multiton.GetInstance();
Multiton instance4 = Multiton.GetInstance(); // Returns existing instance
Console.WriteLine($"Instance 1 ID: {instance1.GetId()}");
Console.WriteLine($"Instance 2 ID: {instance2.GetId()}");
Console.WriteLine($"Instance 3 ID: {instance3.GetId()}");
Console.WriteLine($"Instance 4 ID: {instance4.GetId()}");
}
}
The Multiton pattern ensures that only a limited number of instances of a class can exist. It’s a variation of the Singleton pattern, but instead of a strictly single instance, it allows for a predefined number of instances – “multitons”. This is useful when you need a few dedicated, shared objects with distinct roles, and you want to control their creation to avoid accidental proliferation.
The TypeScript implementation uses a static instanceMap to store the available multitons, keyed by a unique identifier. A static getInstance method retrieves an existing instance or creates a new one if the instance count is below the maximum allowed, and if a valid key is provided. This leverages TypeScript’s static typing and class structure to maintain type safety and encapsulates instance management within the class itself, adhering to modern TypeScript conventions for controlled instantiation.
/**
* The Multiton pattern ensures a limited number of instances.
*/
class Multiton {
private static instanceMap: { [key: string]: Multiton } = {};
private static maxInstances = 3;
private key: string;
private constructor(key: string) {
this.key = key;
}
static getInstance(key: string): Multiton {
if (!Multiton.instanceMap[key]) {
if (Object.keys(Multiton.instanceMap).length < Multiton.maxInstances) {
Multiton.instanceMap[key] = new Multiton(key);
console.log(`Created instance with key: ${key}`);
} else {
throw new Error(`Maximum number of Multiton instances reached (${Multiton.maxInstances}).`);
}
}
return Multiton.instanceMap[key];
}
public getKey(): string {
return this.key;
}
}
// Example Usage:
const instance1 = Multiton.getInstance('A');
const instance2 = Multiton.getInstance('B');
const instance3 = Multiton.getInstance('C');
console.log(instance1.getKey()); // Output: A
console.log(instance2.getKey()); // Output: B
console.log(instance3.getKey()); // Output: C
try {
const instance4 = Multiton.getInstance('D');
} catch (error) {
console.error(error.message); // Output: Maximum number of Multiton instances reached (3).
}
The Multiton pattern ensures that only a specific number of object instances exist for a given class. It’s a constraint of the Singleton pattern. Instead of just one instance, we control the maximum number allowed. This is useful for resource management (e.g., database connections, thread pools) where creating too many instances can lead to performance issues or resource exhaustion. The JavaScript implementation uses a Map to store instances, keyed by a “name” or identifier. The getInstance method checks if an instance with that name already exists; if it doesn’t and the instance count is below the maximum, it creates one and stores it. Using a Map is a clean and efficient way to handle multiple, named instances in JavaScript.
class Multiton {
constructor(name) {
if (Multiton.instances.has(name)) {
return Multiton.instances.get(name);
}
if (Multiton.instanceCount >= Multiton.maxInstances) {
throw new Error(`Maximum number of instances (${Multiton.maxInstances}) reached.`);
}
this.name = name;
Multiton.instanceCount++;
Multiton.instances.set(name, this);
}
static {
Multiton.instances = new Map();
Multiton.maxInstances = 3; // Define the maximum number of instances
Multiton.instanceCount = 0;
}
getName() {
return this.name;
}
}
// Example Usage:
const instance1 = new Multiton("A");
const instance2 = new Multiton("B");
const instance3 = new Multiton("C");
console.log(instance1.getName()); // Output: A
console.log(instance2.getName()); // Output: B
console.log(instance3.getName()); // Output: C
try {
const instance4 = new Multiton("D"); // This will throw an error
} catch (error) {
console.error(error.message); // Output: Maximum number of instances (3) reached.
}
console.log(Multiton.instances.size); // Output: 3
The Multiton pattern ensures that only a limited number of instances of a class exist. Unlike a Singleton which allows only one instance, a Multiton permits a predefined number. This is useful when managing a pool of resources or managing a small set of unique entities. The Python implementation uses a class-level dictionary to store instances, creating them on demand up to the specified maximum limit. The __new__ method is overridden to control instantiation. This is idiomatic Python as it leverages the language’s dynamic nature and uses dictionaries for efficient instance management, avoiding excessive class decoration or complex static initialization.
class Multiton:
"""
Multiton pattern implementation. Limits the number of instances of a class.
"""
_instances = {}
_max_instances = 3 # Define maximum number of instances
def __new__(cls, *args, **kwargs):
if len(cls._instances) < cls._max_instances:
instance = super().__new__(cls)
cls._instances[id(instance)] = instance
return instance
else:
# Return an existing instance if the limit is reached
for inst in cls._instances.values():
return inst
#Should not reach here, but handle case where no instances exist after exceeding limit.
return None
def __init__(self, name):
self.name = name
def __repr__(self):
return f"Multiton instance: {self.name}, id: {id(self)}"
# Example Usage:
if __name__ == "__main__":
instance1 = Multiton("Instance 1")
instance2 = Multiton("Instance 2")
instance3 = Multiton("Instance 3")
instance4 = Multiton("Instance 4") # Will return an existing instance
print(instance1)
print(instance2)
print(instance3)
print(instance4)
print(f"Number of instances: {len(Multiton._instances)}")
The Multiton pattern ensures that only a limited number of instances of a class exist. It’s a relaxation of the Singleton pattern, useful when more than one object is needed but a full-blown factory feels excessive. This Java implementation uses an enum to define the limited set of instances, leveraging the inherent guarantee that enum values are instantiated only once. The enum approach is concise and thread-safe, aligning with Java’s preference for enums when a fixed set of constants with associated state is required, making it more idiomatic than explicit static instance management.
// Multiton.java
public class Multiton {
private final String value;
private Multiton(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public enum Instance {
INSTANCE_ONE("First"),
INSTANCE_TWO("Second"),
INSTANCE_THREE("Third");
private final Multiton multiton;
Instance(String value) {
this.multiton = new Multiton(value);
}
public Multiton getInstance() {
return multiton;
}
}
public static void main(String[] args) {
Multiton instance1 = Multiton.Instance.INSTANCE_ONE.getInstance();
Multiton instance2 = Multiton.Instance.INSTANCE_TWO.getInstance();
Multiton instance3 = Multiton.Instance.INSTANCE_THREE.getInstance();
System.out.println(instance1.getValue());
System.out.println(instance2.getValue());
System.out.println(instance3.getValue());
// Verify that instances are unique
System.out.println(instance1 == instance2); // false
System.out.println(instance1 == instance3); // false
System.out.println(instance2 == instance3); // false
}
}