diff --git a/multiton/README.md b/multiton/README.md index eec858a0f..6e40a4404 100644 --- a/multiton/README.md +++ b/multiton/README.md @@ -3,34 +3,32 @@ title: Multiton category: Creational language: en tag: - - Instantiation + - Decoupling + - Instantiation + - Object composition --- ## Also known as -Registry +* Registry of Singletons ## Intent -Ensure a class only has a limited number of instances and provide a global point of access to them. +The Multiton pattern is a variation of the Singleton design pattern that manages a map of named instances as key-value pairs. ## Explanation Real-world example -> The Nazgûl, also called ringwraiths or the Nine Riders, are Sauron's most terrible servants. By -> definition, there's always nine of them. +> The Nazgûl, also called ringwraiths or the Nine Riders, are Sauron's most terrible servants. By definition, there's always nine of them. In plain words -> Multiton pattern ensures there are a predefined amount of instances available globally. +> Multiton pattern ensures there are a predefined amount of instances available globally. Wikipedia says -> In 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. +> In 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. **Programmatic Example** @@ -39,46 +37,42 @@ Wikipedia says ```java public enum NazgulName { - KHAMUL, MURAZOR, DWAR, JI_INDUR, AKHORAHIL, HOARMURATH, ADUNAPHEL, REN, UVATHA + KHAMUL, MURAZOR, DWAR, JI_INDUR, AKHORAHIL, HOARMURATH, ADUNAPHEL, REN, UVATHA } public final class Nazgul { - private static final Map nazguls; + private static final Map nazguls; - private final NazgulName name; + @Getter + private final NazgulName name; - static { - nazguls = new ConcurrentHashMap<>(); - nazguls.put(NazgulName.KHAMUL, new Nazgul(NazgulName.KHAMUL)); - nazguls.put(NazgulName.MURAZOR, new Nazgul(NazgulName.MURAZOR)); - nazguls.put(NazgulName.DWAR, new Nazgul(NazgulName.DWAR)); - nazguls.put(NazgulName.JI_INDUR, new Nazgul(NazgulName.JI_INDUR)); - nazguls.put(NazgulName.AKHORAHIL, new Nazgul(NazgulName.AKHORAHIL)); - nazguls.put(NazgulName.HOARMURATH, new Nazgul(NazgulName.HOARMURATH)); - nazguls.put(NazgulName.ADUNAPHEL, new Nazgul(NazgulName.ADUNAPHEL)); - nazguls.put(NazgulName.REN, new Nazgul(NazgulName.REN)); - nazguls.put(NazgulName.UVATHA, new Nazgul(NazgulName.UVATHA)); - } + static { + nazguls = new ConcurrentHashMap<>(); + nazguls.put(NazgulName.KHAMUL, new Nazgul(NazgulName.KHAMUL)); + nazguls.put(NazgulName.MURAZOR, new Nazgul(NazgulName.MURAZOR)); + nazguls.put(NazgulName.DWAR, new Nazgul(NazgulName.DWAR)); + nazguls.put(NazgulName.JI_INDUR, new Nazgul(NazgulName.JI_INDUR)); + nazguls.put(NazgulName.AKHORAHIL, new Nazgul(NazgulName.AKHORAHIL)); + nazguls.put(NazgulName.HOARMURATH, new Nazgul(NazgulName.HOARMURATH)); + nazguls.put(NazgulName.ADUNAPHEL, new Nazgul(NazgulName.ADUNAPHEL)); + nazguls.put(NazgulName.REN, new Nazgul(NazgulName.REN)); + nazguls.put(NazgulName.UVATHA, new Nazgul(NazgulName.UVATHA)); + } - private Nazgul(NazgulName name) { - this.name = name; - } + private Nazgul(NazgulName name) { + this.name = name; + } - public static Nazgul getInstance(NazgulName name) { - return nazguls.get(name); - } - - public NazgulName getName() { - return name; - } + public static Nazgul getInstance(NazgulName name) { + return nazguls.get(name); + } } ``` And here's how we access the `Nazgul` instances. ```java -// eagerly initialized multiton LOGGER.info("Printing out eagerly initialized multiton contents"); LOGGER.info("KHAMUL={}", Nazgul.getInstance(NazgulName.KHAMUL)); LOGGER.info("MURAZOR={}", Nazgul.getInstance(NazgulName.MURAZOR)); @@ -89,18 +83,6 @@ LOGGER.info("HOARMURATH={}", Nazgul.getInstance(NazgulName.HOARMURATH)); LOGGER.info("ADUNAPHEL={}", Nazgul.getInstance(NazgulName.ADUNAPHEL)); LOGGER.info("REN={}", Nazgul.getInstance(NazgulName.REN)); LOGGER.info("UVATHA={}", Nazgul.getInstance(NazgulName.UVATHA)); - -// enum multiton -LOGGER.info("Printing out enum-based multiton contents"); -LOGGER.info("KHAMUL={}", NazgulEnum.KHAMUL); -LOGGER.info("MURAZOR={}", NazgulEnum.MURAZOR); -LOGGER.info("DWAR={}", NazgulEnum.DWAR); -LOGGER.info("JI_INDUR={}", NazgulEnum.JI_INDUR); -LOGGER.info("AKHORAHIL={}", NazgulEnum.AKHORAHIL); -LOGGER.info("HOARMURATH={}", NazgulEnum.HOARMURATH); -LOGGER.info("ADUNAPHEL={}", NazgulEnum.ADUNAPHEL); -LOGGER.info("REN={}", NazgulEnum.REN); -LOGGER.info("UVATHA={}", NazgulEnum.UVATHA); ``` Program output: @@ -116,25 +98,42 @@ Program output: 20:35:07.419 [main] INFO com.iluwatar.multiton.App - ADUNAPHEL=com.iluwatar.multiton.Nazgul@77556fd 20:35:07.419 [main] INFO com.iluwatar.multiton.App - REN=com.iluwatar.multiton.Nazgul@368239c8 20:35:07.420 [main] INFO com.iluwatar.multiton.App - UVATHA=com.iluwatar.multiton.Nazgul@9e89d68 -20:35:07.420 [main] INFO com.iluwatar.multiton.App - Printing out enum-based multiton contents -20:35:07.420 [main] INFO com.iluwatar.multiton.App - KHAMUL=KHAMUL -20:35:07.420 [main] INFO com.iluwatar.multiton.App - MURAZOR=MURAZOR -20:35:07.420 [main] INFO com.iluwatar.multiton.App - DWAR=DWAR -20:35:07.420 [main] INFO com.iluwatar.multiton.App - JI_INDUR=JI_INDUR -20:35:07.421 [main] INFO com.iluwatar.multiton.App - AKHORAHIL=AKHORAHIL -20:35:07.421 [main] INFO com.iluwatar.multiton.App - HOARMURATH=HOARMURATH -20:35:07.421 [main] INFO com.iluwatar.multiton.App - ADUNAPHEL=ADUNAPHEL -20:35:07.421 [main] INFO com.iluwatar.multiton.App - REN=REN -20:35:07.421 [main] INFO com.iluwatar.multiton.App - UVATHA=UVATHA ``` ## Class diagram -![alt text](./etc/multiton.png "Multiton") +![Multiton](./etc/multiton.png "Multiton") ## Applicability Use the Multiton pattern when -* There must be a specific number of instances of a class, and they must be accessible to clients from -a well-known access point. +* A class must have named instances, but only one instance for each unique key. +* Global access to these instances is necessary without requiring global variables. +* You want to manage shared resources categorized by key. + +## Known Uses + +* Managing database connections in different contexts. +* Configuration settings for different environments in an application. + +## Consequences + +Benefits: + +* Ensures controlled access to instances based on key. +* Reduces global state usage by encapsulating instance management within the pattern. + +Trade-offs: + +* Increased memory usage if not managed properly due to multiple instances. +* Potential issues with concurrency if not implemented with thread safety in mind. + +## Related Patterns + +[Singleton](https://java-design-patterns.com/patterns/singleton/): Multiton can be seen as an extension of the Singleton pattern where Singleton allows only one instance of a class, Multiton allows one instance per key. +[Factory Method](https://java-design-patterns.com/patterns/factory-method/): Multiton uses a method to create or retrieve instances, similar to how a Factory Method controls object creation. + +## Credits + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI) diff --git a/multiton/src/main/java/com/iluwatar/multiton/App.java b/multiton/src/main/java/com/iluwatar/multiton/App.java index 80aa9a4c8..5e932a86b 100644 --- a/multiton/src/main/java/com/iluwatar/multiton/App.java +++ b/multiton/src/main/java/com/iluwatar/multiton/App.java @@ -32,7 +32,7 @@ import lombok.extern.slf4j.Slf4j; * the Multiton by passing an enumeration as a parameter. * *

There is more than one way to implement the multiton design pattern. In the first example - * {@link Nazgul} is the Multiton and we can ask single {@link Nazgul} from it using {@link + * {@link Nazgul} is the Multiton, and we can ask single {@link Nazgul} from it using {@link * NazgulName}. The {@link Nazgul}s are statically initialized and stored in a concurrent hash map. * *

In the enum implementation {@link NazgulEnum} is the multiton. It is static and mutable diff --git a/multiton/src/main/java/com/iluwatar/multiton/Nazgul.java b/multiton/src/main/java/com/iluwatar/multiton/Nazgul.java index 2010f0565..35e7ea337 100644 --- a/multiton/src/main/java/com/iluwatar/multiton/Nazgul.java +++ b/multiton/src/main/java/com/iluwatar/multiton/Nazgul.java @@ -26,6 +26,7 @@ package com.iluwatar.multiton; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import lombok.Getter; /** * Nazgul is a Multiton class. Nazgul instances can be queried using {@link #getInstance} method. @@ -34,6 +35,7 @@ public final class Nazgul { private static final Map nazguls; + @Getter private final NazgulName name; static { @@ -56,8 +58,4 @@ public final class Nazgul { public static Nazgul getInstance(NazgulName name) { return nazguls.get(name); } - - public NazgulName getName() { - return name; - } }