docs: update multiton

This commit is contained in:
Ilkka Seppälä committed 2024-05-09 13:42:25 +03:00
1 parent de1dd7f82a
commit 6cc4525bc1
3 files changed
+63 -66

No files matched your search

+60 -61
View File
@@ -3,34 +3,32 @@ title: Multiton
category: Creational category: Creational
language: en language: en
tag: tag:
- Instantiation - Decoupling
- Instantiation
- Object composition
--- ---
## Also known as ## Also known as
Registry * Registry of Singletons
## Intent ## 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 ## Explanation
Real-world example Real-world example
> The Nazgûl, also called ringwraiths or the Nine Riders, are Sauron's most terrible servants. By > The Nazgûl, also called ringwraiths or the Nine Riders, are Sauron's most terrible servants. By definition, there's always nine of them.
> definition, there's always nine of them.
In plain words 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 Wikipedia says
> In software engineering, the multiton pattern is a design pattern which generalizes the singleton > 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.
> 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** **Programmatic Example**
@@ -39,46 +37,42 @@ Wikipedia says
```java ```java
public enum NazgulName { 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 { public final class Nazgul {
private static final Map<NazgulName, Nazgul> nazguls; private static final Map<NazgulName, Nazgul> nazguls;
private final NazgulName name; @Getter
private final NazgulName name;
static { static {
nazguls = new ConcurrentHashMap<>(); nazguls = new ConcurrentHashMap<>();
nazguls.put(NazgulName.KHAMUL, new Nazgul(NazgulName.KHAMUL)); nazguls.put(NazgulName.KHAMUL, new Nazgul(NazgulName.KHAMUL));
nazguls.put(NazgulName.MURAZOR, new Nazgul(NazgulName.MURAZOR)); nazguls.put(NazgulName.MURAZOR, new Nazgul(NazgulName.MURAZOR));
nazguls.put(NazgulName.DWAR, new Nazgul(NazgulName.DWAR)); nazguls.put(NazgulName.DWAR, new Nazgul(NazgulName.DWAR));
nazguls.put(NazgulName.JI_INDUR, new Nazgul(NazgulName.JI_INDUR)); nazguls.put(NazgulName.JI_INDUR, new Nazgul(NazgulName.JI_INDUR));
nazguls.put(NazgulName.AKHORAHIL, new Nazgul(NazgulName.AKHORAHIL)); nazguls.put(NazgulName.AKHORAHIL, new Nazgul(NazgulName.AKHORAHIL));
nazguls.put(NazgulName.HOARMURATH, new Nazgul(NazgulName.HOARMURATH)); nazguls.put(NazgulName.HOARMURATH, new Nazgul(NazgulName.HOARMURATH));
nazguls.put(NazgulName.ADUNAPHEL, new Nazgul(NazgulName.ADUNAPHEL)); nazguls.put(NazgulName.ADUNAPHEL, new Nazgul(NazgulName.ADUNAPHEL));
nazguls.put(NazgulName.REN, new Nazgul(NazgulName.REN)); nazguls.put(NazgulName.REN, new Nazgul(NazgulName.REN));
nazguls.put(NazgulName.UVATHA, new Nazgul(NazgulName.UVATHA)); nazguls.put(NazgulName.UVATHA, new Nazgul(NazgulName.UVATHA));
} }
private Nazgul(NazgulName name) { private Nazgul(NazgulName name) {
this.name = name; this.name = name;
} }
public static Nazgul getInstance(NazgulName name) { public static Nazgul getInstance(NazgulName name) {
return nazguls.get(name); return nazguls.get(name);
} }
public NazgulName getName() {
return name;
}
} }
``` ```
And here's how we access the `Nazgul` instances. And here's how we access the `Nazgul` instances.
```java ```java
// eagerly initialized multiton
LOGGER.info("Printing out eagerly initialized multiton contents"); LOGGER.info("Printing out eagerly initialized multiton contents");
LOGGER.info("KHAMUL={}", Nazgul.getInstance(NazgulName.KHAMUL)); LOGGER.info("KHAMUL={}", Nazgul.getInstance(NazgulName.KHAMUL));
LOGGER.info("MURAZOR={}", Nazgul.getInstance(NazgulName.MURAZOR)); 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("ADUNAPHEL={}", Nazgul.getInstance(NazgulName.ADUNAPHEL));
LOGGER.info("REN={}", Nazgul.getInstance(NazgulName.REN)); LOGGER.info("REN={}", Nazgul.getInstance(NazgulName.REN));
LOGGER.info("UVATHA={}", Nazgul.getInstance(NazgulName.UVATHA)); 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: 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 - 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.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 - 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 ## Class diagram
![alt text](./etc/multiton.png "Multiton") ![Multiton](./etc/multiton.png "Multiton")
## Applicability ## Applicability
Use the Multiton pattern when 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 class must have named instances, but only one instance for each unique key.
a well-known access point. * 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)
@@ -32,7 +32,7 @@ import lombok.extern.slf4j.Slf4j;
* the Multiton by passing an enumeration as a parameter. * the Multiton by passing an enumeration as a parameter.
* *
* <p>There is more than one way to implement the multiton design pattern. In the first example * <p>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. * NazgulName}. The {@link Nazgul}s are statically initialized and stored in a concurrent hash map.
* *
* <p>In the enum implementation {@link NazgulEnum} is the multiton. It is static and mutable * <p>In the enum implementation {@link NazgulEnum} is the multiton. It is static and mutable
@@ -26,6 +26,7 @@ package com.iluwatar.multiton;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import lombok.Getter;
/** /**
* Nazgul is a Multiton class. Nazgul instances can be queried using {@link #getInstance} method. * 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<NazgulName, Nazgul> nazguls; private static final Map<NazgulName, Nazgul> nazguls;
@Getter
private final NazgulName name; private final NazgulName name;
static { static {
@@ -56,8 +58,4 @@ public final class Nazgul {
public static Nazgul getInstance(NazgulName name) { public static Nazgul getInstance(NazgulName name) {
return nazguls.get(name); return nazguls.get(name);
} }
public NazgulName getName() {
return name;
}
} }