Recent from talks
Contribute something
Nothing was collected or created yet.
Multiton pattern
View on WikipediaIn software engineering, the multiton pattern is a design pattern which generalizes the singleton pattern. Whereas the singleton allows only one instance of a class to be created, the multiton pattern allows for the controlled creation of multiple instances, which it manages through the use of a map.
Rather than having a single instance per application (e.g. the java.lang.Runtime object in the Java programming language) the multiton pattern instead ensures a single instance per key.
The multiton pattern does not explicitly appear as a pattern in the highly regarded object-oriented programming textbook Design Patterns.[1] However, the book describes using a registry of singletons to allow subclassing of singletons,[2] which is essentially the multiton pattern.[citation needed]
Description
[edit]While it may appear that the multiton is a hash table with synchronized access there are two important distinctions. First, the multiton does not allow clients to add mappings. Secondly, the multiton never returns a null or empty reference; instead, it creates and stores a multiton instance on the first request with the associated key. Subsequent requests with the same key return the original instance. A hash table is merely an implementation detail and not the only possible approach. The pattern simplifies retrieval of shared objects in an application.
Since the object pool is created only once, being a member associated with the class (instead of the instance), the multiton retains its flat behavior rather than evolving into a tree structure.
The multiton is unique in that it provides centralized access to a single directory (i.e. all keys are in the same namespace, per se) of multitons, where each multiton instance in the pool may exist having its own state. In this manner, the pattern advocates indexed storage of essential objects for the system (such as would be provided by an LDAP system, for example). However, a multiton is limited to wide use by a single system rather than a myriad of distributed systems.
Drawbacks
[edit]This pattern, like the Singleton pattern, makes unit testing far more difficult,[3] as it introduces global state into an application.
With garbage collected languages it may become a source of memory leaks as it introduces global strong references to the objects.
Implementations
[edit]In Java, the multiton pattern can be implemented using an enumerated type, with the values of the type corresponding to the instances. In the case of an enumerated type with a single value, this gives the singleton pattern.
In C#, we can also use enums, as the following example shows:
using System;
using System.Collections.Generic;
public enum MultitonType
{
Zero,
One,
Two
}
public class Multiton
{
private static readonly Dictionary<MultitonType, Multiton> instances =
new Dictionary<MultitonType, Multiton>();
private MultitonType type;
private Multiton(MultitonType type)
{
this.type = type;
}
public static Multiton GetInstance(MultitonType type)
{
// Lazy init (not thread safe as written)
// Recommend using Double Check Locking if needing thread safety
if (!instances.TryGetValue(type, out var instance))
{
instance = new Multiton(type);
instances.Add(type, instance);
}
return instance;
}
public override string ToString()
{
return $"My type is {this.type}";
}
// Sample usage
public static void Main(string[] args)
{
Multiton m0 = Multiton.GetInstance(MultitonType.Zero);
Multiton m1 = Multiton.GetInstance(MultitonType.One);
Multiton m2 = Multiton.GetInstance(MultitonType.Two);
Console.WriteLine(m0);
Console.WriteLine(m1);
Console.WriteLine(m2);
}
}
References
[edit]- ^ O'Docherty, Mike (2005). Object-oriented analysis and design: understanding system development with UML 2.0. Chichester: Wiley. p. 341. ISBN 0470092408.
- ^ Design patterns: elements of reusable object-oriented software. Boston, Mass. Munich: Addison-Wesley. 2011. p. 130. ISBN 0-201-63361-2.
- ^ "Clean Code Talks - Global State and Singletons".
External links
[edit]Multiton pattern
View on GrokipediaDefinition and Purpose
Core Concept
The Multiton pattern is a creational design pattern that serves as a generalization of the Singleton pattern, permitting a controlled multiplicity of class instances where each is uniquely identified by a key, such as a string or an enumeration value.[5][6] This approach ensures that instances are created and managed deliberately, associating exactly one instance per distinct key to avoid unnecessary duplication while supporting a finite set of related objects. Instances managed by the Multiton are maintained in a central registry, often realized as a hash map or associative array, which facilitates global access across the application and enforces the uniqueness constraint based on keys.[5][6] By centralizing storage, the pattern prevents the direct instantiation of the class via constructors, routing all requests through a retrieval mechanism that checks for existing entries before creating new ones. This registry acts as the sole point of control, promoting consistency and resource sharing. The primary intent of the Multiton pattern is to enable efficient resource management through restricted object creation, particularly for scenarios where multiple similar but keyed instances are needed without allowing arbitrary proliferation of objects.[5] It addresses the limitations of unrestricted instantiation by providing a structured way to reuse pre-existing instances for repeated key requests, thereby optimizing performance in contexts like caching or configuration handling. The Singleton pattern represents a special case of the Multiton, limited to a single instance under a fixed key.[6] A basic pseudocode representation of instance retrieval in the Multiton pattern illustrates its mechanics:function getInstance(key):
if registry contains key:
return registry[key]
else:
newInstance = createNewInstance(key)
registry[key] = newInstance
return newInstance
function getInstance(key):
if registry contains key:
return registry[key]
else:
newInstance = createNewInstance(key)
registry[key] = newInstance
return newInstance
Key Characteristics
The Multiton pattern maintains several core invariants that distinguish it from standard object-oriented instantiation. A fixed or bounded number of class instances is enforced, preventing uncontrolled proliferation while allowing multiple instances based on predefined criteria. Each instance is uniquely tied to an immutable key, ensuring that requests for the same key always retrieve the identical object, thereby promoting consistency and predictability in object identity. Access to these instances is provided through a global static method, which serves as the sole entry point for retrieval and acts as a centralized registry.[7][8] Behaviorally, the pattern incorporates lazy initialization, where instances are created only upon the first request for a specific key rather than preemptively, optimizing resource usage by deferring allocation until necessary. The creation logic is fully encapsulated within the class itself, hiding the complexity of instance management from clients and centralizing control over the lifecycle of objects. This encapsulation often involves an internal map data structure to associate keys with instances, as referenced in foundational descriptions of the pattern.[7] In contrast to regular object creation, the Multiton pattern eliminates public constructors, routing all instantiation through the key-based factory method to enforce the bounded instance policy and prevent direct object spawning. This design ensures that clients cannot bypass the controlled access mechanism, maintaining the integrity of the instance pool. The key plays a pivotal role in this architecture, typically implemented as an enum, string, or integer, which guarantees orthogonality by providing a distinct, non-overlapping identifier for each instance variant.[7]Relation to Other Patterns
Comparison with Singleton
The Singleton and Multiton patterns share fundamental similarities in their approach to creational control, both ensuring that instances of a class are created and managed in a controlled manner while providing a global access point for retrieval.[7][9] The Multiton can be considered a direct generalization of the Singleton, where the latter operates with a fixed, implicit key (such as null or empty) that permits only a single instance.[7] Key differences arise in their scope of instantiation: the Singleton strictly limits a class to exactly one instance, eliminating the need for any identification key and enforcing uniqueness across the application.[10] In contrast, the Multiton supports the creation and management of multiple instances, each uniquely identified by a key, enabling greater flexibility for scenarios like resource pooling where several similar but distinct objects are required.[7][9] This multiplicity in the Multiton enhances scalability by allowing controlled expansion beyond a single resource, whereas the Singleton's rigidity suits cases demanding absolute uniqueness. The Singleton is typically chosen over the Multiton when a truly unique global resource is essential, such as an application-wide configuration manager or a centralized logging service that must remain singular to maintain consistency.[10] Historically, the Multiton emerged as a non-GoF design pattern specifically to overcome the limitations of the Singleton's single-instance constraint in situations requiring multiple analogous instances under controlled access.[9]Comparison with Flyweight
The Multiton and Flyweight patterns both utilize a central registry, often implemented as a factory or map, to manage and share instances based on keys, thereby minimizing redundant object creation and promoting efficient resource use.[7][11] Despite this commonality, the patterns diverge significantly in their handling of state and instance management. The Flyweight pattern, classified as a structural design pattern in the Gang of Four catalog, relies on separating an object's intrinsic state—immutable data shared across instances, such as a glyph's font metrics—from its extrinsic state, which is context-dependent and passed at runtime, enabling fine-grained sharing of lightweight, immutable objects in memory-intensive applications like graphics systems. In comparison, the Multiton pattern, an extension of the creational Singleton pattern outside the original Gang of Four framework, maintains a predefined, fixed set of distinct instances keyed for uniqueness, without mandating state separation; these instances can be mutable and represent variations like different resource configurations.[7] Flyweight is preferable over Multiton when dealing with vast numbers of nearly identical objects that benefit from extrinsic state parameterization, as in character rendering within text editors where shared intrinsic properties like shape and color reduce memory footprint while allowing positional variability.[11] Multiton, by contrast, suits scenarios requiring a controlled pool of unique, non-interchangeable instances without the overhead of state partitioning.[7] Although Flyweight forms part of the seminal Gang of Four catalog as a structural pattern, Multiton emerged later as a creational pattern generalizing Singleton principles and is absent from that foundational text.[7]Benefits and Limitations
Advantages
The Multiton pattern enhances resource efficiency by restricting the creation of class instances to a predefined, controlled number, thereby mitigating memory bloat in resource-intensive scenarios such as connection pools or caching mechanisms where unbounded instantiation could lead to excessive resource consumption.[12] This controlled allocation is particularly beneficial in environments with limited resources, as it promotes reuse of existing instances keyed by unique identifiers, reducing the overhead associated with repeated object creation and garbage collection.[7] By centralizing instance management through a registry or map, the Multiton pattern ensures consistency throughout the application, guaranteeing that all components access the identical set of instances for a given key and thereby preventing duplication, fragmented state management, and potential inconsistencies that could arise from uncoordinated object creation.[12] This uniform access mechanism fosters reliable behavior across distributed or modular systems, similar to how the Singleton pattern maintains consistency for a single instance but extended to handle multiple keyed variants.[7] The pattern further supports lazy loading, wherein instances are instantiated only upon the first request for a specific key, which optimizes application startup times and runtime performance by deferring unnecessary computations and allocations until they are explicitly required.[12] Additionally, it provides strong encapsulation by abstracting the intricacies of instance tracking, validation, and retrieval behind a simple, uniform interface, thereby simplifying client code and reducing the cognitive load on developers who interact with the managed instances.[7]Disadvantages
The Multiton pattern introduces additional complexity through the management of keys and the maintenance of an instance map, which can complicate code maintainability relative to direct object creation. This overhead arises from the need to handle registry logic, potentially leading to more intricate class designs that are harder to understand and extend.[7] Furthermore, the pattern's reliance on global state amplifies this complexity, as it creates shared resources that must be carefully coordinated across the application.[6] In multi-threaded applications, implementing the Multiton pattern necessitates synchronization mechanisms, such as locks or thread-safe collections, to prevent concurrent modifications to the instance map. These measures can create performance bottlenecks by enforcing serialized access, reducing throughput in high-concurrency scenarios.[7] Without proper handling, race conditions may arise during instance creation or retrieval, further exacerbating reliability issues.[13] The global state inherent in the Multiton pattern poses significant challenges for unit testing, as shared instances can persist across test executions, leading to unintended interference and non-deterministic outcomes. Testers often require explicit cleanup routines or advanced mocking techniques to reset the state between runs, increasing the effort and fragility of test suites.[13] This vulnerability to side effects, akin to those in global shared buffers, makes isolation of components more difficult and can hinder overall test coverage.[13] Regarding memory management, the pattern risks unbounded growth if the set of keys is not explicitly limited, as each new key can spawn a persistent instance that remains referenced in the map and thus ineligible for garbage collection. This can result in excessive memory consumption over time, particularly in long-running applications where key proliferation occurs without eviction policies.[7] In resource-constrained environments, such accumulation may degrade performance or lead to out-of-memory errors if not monitored.[7]Implementations and Applications
General Implementation Approach
The general implementation approach for the Multiton pattern follows a structured methodology to manage a controlled set of instances, each associated with a unique key, extending the principles of the Singleton pattern to support multiple keyed instances while ensuring global access and preventing redundant creation. This language-agnostic blueprint emphasizes encapsulation, lazy loading, and centralized control through a registry mechanism. A foundational step is to define a private static map (or equivalent associative data structure) within the class to store key-instance pairs. This map acts as the central registry, enabling efficient lookup, storage, and retrieval of instances based on their identifying keys, which could be strings, enums, or other immutable types. The map is typically initialized as empty to support on-demand population, aligning with lazy initialization characteristics of the pattern.[8] The core access mechanism involves providing a public static factory method that accepts a key parameter. This method first queries the map for an existing instance corresponding to the key; if none is found, it invokes the private constructor to create a new instance, associates it with the key, and adds the pair to the map before returning the instance. If an instance already exists, it is simply retrieved and returned, ensuring that only one instance per key is ever created. To enforce exclusivity, all class constructors must be declared private, prohibiting direct instantiation from client code and routing all requests through the factory method. This step guarantees thread-safe access in concurrent environments if the map and method are appropriately synchronized, though basic implementations may require additional locking for production use.[8][14] To address robustness, edge cases must be handled explicitly in the factory method, such as validating the input key and throwing an exception (e.g., for null or malformed keys) to prevent invalid entries in the registry. Optional cleanup functionality can include a static method to remove a specific instance from the map by key, facilitating resource reclamation in scenarios where instances are dynamically managed or when application lifecycle demands it.[8]Language-Specific Examples and Use Cases
In Java, the Multiton pattern can be implemented using aConcurrentHashMap to manage instances keyed by an enum for type safety and inherent thread safety, as Java enums ensure singleton-like behavior per value. This approach allows lazy or preloaded instantiation while avoiding concurrent modification issues.[15]
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public final class Multiton {
private enum Key { TYPE_A, TYPE_B, TYPE_C }
private static final Map<Key, Multiton> instances = new ConcurrentHashMap<>();
private final Key key;
private Multiton(Key key) {
this.key = key;
}
public static Multiton getInstance(Key key) {
return instances.computeIfAbsent(key, Multiton::new);
}
@Override
public String toString() {
return "Multiton instance for key: " + key;
}
}
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public final class Multiton {
private enum Key { TYPE_A, TYPE_B, TYPE_C }
private static final Map<Key, Multiton> instances = new ConcurrentHashMap<>();
private final Key key;
private Multiton(Key key) {
this.key = key;
}
public static Multiton getInstance(Key key) {
return instances.computeIfAbsent(key, Multiton::new);
}
@Override
public String toString() {
return "Multiton instance for key: " + key;
}
}
computeIfAbsent method handles thread-safe insertion atomically.[15]
In C#, the pattern employs a ConcurrentDictionary<TKey, Instance> with lazy initialization using GetOrAdd to ensure thread safety during instance creation, minimizing contention for subsequent accesses. The method atomically retrieves or adds the instance for a given key.[16]
using System;
using System.Collections.Concurrent;
public class Multiton<TKey> {
private static readonly ConcurrentDictionary<TKey, Multiton<TKey>> instances = new ConcurrentDictionary<TKey, Multiton<TKey>>();
private readonly TKey key;
private Multiton(TKey key) {
this.key = key;
}
public static Multiton<TKey> GetInstance(TKey key) {
return instances.GetOrAdd(key, k => new Multiton<TKey>(k));
}
public override string ToString() {
return $"Multiton instance for key: {key}";
}
}
using System;
using System.Collections.Concurrent;
public class Multiton<TKey> {
private static readonly ConcurrentDictionary<TKey, Multiton<TKey>> instances = new ConcurrentDictionary<TKey, Multiton<TKey>>();
private readonly TKey key;
private Multiton(TKey key) {
this.key = key;
}
public static Multiton<TKey> GetInstance(TKey key) {
return instances.GetOrAdd(key, k => new Multiton<TKey>(k));
}
public override string ToString() {
return $"Multiton instance for key: {key}";
}
}
GetInstance atomically retrieves or creates the instance, supporting keys like strings or enums.[16]
In Python, a simple implementation uses a dictionary for the registry with a class method for access, suitable for single-threaded or synchronized environments.[17]
class Multiton:
_instances = {}
def __new__(cls, key):
if key not in cls._instances:
cls._instances[key] = super(Multiton, cls).__new__(cls)
return cls._instances[key]
def __init__(self, key):
self.key = key
def __str__(self):
return f"Multiton instance for key: {self.key}"
class Multiton:
_instances = {}
def __new__(cls, key):
if key not in cls._instances:
cls._instances[key] = super(Multiton, cls).__new__(cls)
return cls._instances[key]
def __init__(self, key):
self.key = key
def __str__(self):
return f"Multiton instance for key: {self.key}"