Compare commits

...

2 Commits

Author SHA1 Message Date
58b7d0115b update multiton example 2022-01-16 20:36:40 +02:00
07ee94d671 refactoring: execute around idiom (#1945)
* Refactor execute around the idiom

* fix checkstyle errors

Co-authored-by: Subhrodip Mohanta <hello@subho.xyz>
2022-01-10 17:04:19 +05:30
6 changed files with 107 additions and 51 deletions

View File

@ -17,10 +17,10 @@ the user to specify only what to do with the resource.
## Explanation
Real world example
Real-world example
> We need to provide a class that can be used to write text strings to files. To make it easy for
> the user we let our service class open and close the file automatically, the user only has to
> A class needs to be provided for writing text strings to files. To make it easy for
> the user, the service class opens and closes the file automatically. The user only has to
> specify what is written into which file.
In plain words
@ -35,35 +35,50 @@ In plain words
**Programmatic Example**
Let's introduce our file writer class.
`SimpleFileWriter` class implements the Execute Around idiom. It takes `FileWriterAction` as a
constructor argument allowing the user to specify what gets written into the file.
```java
@FunctionalInterface
public interface FileWriterAction {
void writeFile(FileWriter writer) throws IOException;
}
@Slf4j
public class SimpleFileWriter {
public SimpleFileWriter(String filename, FileWriterAction action) throws IOException {
LOGGER.info("Opening the file");
try (var writer = new FileWriter(filename)) {
LOGGER.info("Executing the action");
action.writeFile(writer);
LOGGER.info("Closing the file");
}
}
}
```
To utilize the file writer the following code is needed.
The following code demonstrates how `SimpleFileWriter` is used. `Scanner` is used to print the file
contents after the writing finishes.
```java
FileWriterAction writeHello = writer -> {
writer.write("Hello");
writer.append(" ");
writer.append("there!");
};
new SimpleFileWriter("testfile.txt", writeHello);
FileWriterAction writeHello = writer -> {
writer.write("Gandalf was here");
};
new SimpleFileWriter("testfile.txt", writeHello);
var scanner = new Scanner(new File("testfile.txt"));
while (scanner.hasNextLine()) {
LOGGER.info(scanner.nextLine());
}
```
Here's the console output.
```
21:18:07.185 [main] INFO com.iluwatar.execute.around.SimpleFileWriter - Opening the file
21:18:07.188 [main] INFO com.iluwatar.execute.around.SimpleFileWriter - Executing the action
21:18:07.189 [main] INFO com.iluwatar.execute.around.SimpleFileWriter - Closing the file
21:18:07.199 [main] INFO com.iluwatar.execute.around.App - Gandalf was here
```
## Class diagram
@ -74,8 +89,7 @@ To utilize the file writer the following code is needed.
Use the Execute Around idiom when
* You use an API that requires methods to be called in pairs such as open/close or
allocate/deallocate.
* An API requires methods to be called in pairs such as open/close or allocate/deallocate.
## Credits

View File

@ -23,10 +23,14 @@
package com.iluwatar.execute.around;
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
import lombok.extern.slf4j.Slf4j;
/**
* The Execute Around idiom specifies some code to be executed before and after a method. Typically
* The Execute Around idiom specifies executable code before and after a method. Typically
* the idiom is used when the API has methods to be executed in pairs, such as resource
* allocation/deallocation or lock acquisition/release.
*
@ -34,6 +38,7 @@ import java.io.IOException;
* the user. The user specifies only what to do with the file by providing the {@link
* FileWriterAction} implementation.
*/
@Slf4j
public class App {
/**
@ -41,11 +46,16 @@ public class App {
*/
public static void main(String[] args) throws IOException {
// create the file writer and execute the custom action
FileWriterAction writeHello = writer -> {
writer.write("Hello");
writer.append(" ");
writer.append("there!");
writer.write("Gandalf was here");
};
new SimpleFileWriter("testfile.txt", writeHello);
// print the file contents
var scanner = new Scanner(new File("testfile.txt"));
while (scanner.hasNextLine()) {
LOGGER.info(scanner.nextLine());
}
}
}

View File

@ -26,18 +26,24 @@ package com.iluwatar.execute.around;
import java.io.FileWriter;
import java.io.IOException;
import lombok.extern.slf4j.Slf4j;
/**
* SimpleFileWriter handles opening and closing file for the user. The user only has to specify what
* to do with the file resource through {@link FileWriterAction} parameter.
*/
@Slf4j
public class SimpleFileWriter {
/**
* Constructor.
*/
public SimpleFileWriter(String filename, FileWriterAction action) throws IOException {
LOGGER.info("Opening the file");
try (var writer = new FileWriter(filename)) {
LOGGER.info("Executing the action");
action.writeFile(writer);
LOGGER.info("Closing the file");
}
}
}

View File

@ -15,18 +15,18 @@ Registry
## Intent
Ensure a class only has limited number of instances and provide a global point of access to them.
Ensure a class only has a limited number of instances and provide a global point of access to them.
## Explanation
Real world example
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.
> definition, there's always nine of them.
In plain words
> Multiton pattern ensures there's predefined amount of instances available globally.
> Multiton pattern ensures there are a predefined amount of instances available globally.
Wikipedia says
@ -81,29 +81,54 @@ public final class Nazgul {
And here's how we access the `Nazgul` instances.
```java
LOGGER.info("KHAMUL={}", Nazgul.getInstance(NazgulName.KHAMUL));
LOGGER.info("MURAZOR={}", Nazgul.getInstance(NazgulName.MURAZOR));
LOGGER.info("DWAR={}", Nazgul.getInstance(NazgulName.DWAR));
LOGGER.info("JI_INDUR={}", Nazgul.getInstance(NazgulName.JI_INDUR));
LOGGER.info("AKHORAHIL={}", Nazgul.getInstance(NazgulName.AKHORAHIL));
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));
// 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));
LOGGER.info("DWAR={}", Nazgul.getInstance(NazgulName.DWAR));
LOGGER.info("JI_INDUR={}", Nazgul.getInstance(NazgulName.JI_INDUR));
LOGGER.info("AKHORAHIL={}", Nazgul.getInstance(NazgulName.AKHORAHIL));
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:
```
KHAMUL=com.iluwatar.multiton.Nazgul@2b214b94
MURAZOR=com.iluwatar.multiton.Nazgul@17814b1c
DWAR=com.iluwatar.multiton.Nazgul@7ac9af2a
JI_INDUR=com.iluwatar.multiton.Nazgul@7bb004b8
AKHORAHIL=com.iluwatar.multiton.Nazgul@78e89bfe
HOARMURATH=com.iluwatar.multiton.Nazgul@652ce654
ADUNAPHEL=com.iluwatar.multiton.Nazgul@522ba524
REN=com.iluwatar.multiton.Nazgul@29c5ee1d
UVATHA=com.iluwatar.multiton.Nazgul@15cea7b0
20:35:07.413 [main] INFO com.iluwatar.multiton.App - Printing out eagerly initialized multiton contents
20:35:07.417 [main] INFO com.iluwatar.multiton.App - KHAMUL=com.iluwatar.multiton.Nazgul@48cf768c
20:35:07.419 [main] INFO com.iluwatar.multiton.App - MURAZOR=com.iluwatar.multiton.Nazgul@7960847b
20:35:07.419 [main] INFO com.iluwatar.multiton.App - DWAR=com.iluwatar.multiton.Nazgul@6a6824be
20:35:07.419 [main] INFO com.iluwatar.multiton.App - JI_INDUR=com.iluwatar.multiton.Nazgul@5c8da962
20:35:07.419 [main] INFO com.iluwatar.multiton.App - AKHORAHIL=com.iluwatar.multiton.Nazgul@512ddf17
20:35:07.419 [main] INFO com.iluwatar.multiton.App - HOARMURATH=com.iluwatar.multiton.Nazgul@2c13da15
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
@ -114,5 +139,5 @@ UVATHA=com.iluwatar.multiton.Nazgul@15cea7b0
Use the Multiton pattern when
* There must be specific number of instances of a class, and they must be accessible to clients from
* There must be a specific number of instances of a class, and they must be accessible to clients from
a well-known access point.

View File

@ -26,13 +26,13 @@ package com.iluwatar.multiton;
import lombok.extern.slf4j.Slf4j;
/**
* Whereas Singleton design pattern introduces single globally accessible object the Multiton
* Whereas Singleton design pattern introduces single globally accessible object, the Multiton
* pattern defines many globally accessible objects. The client asks for the correct instance from
* the Multiton by passing an enumeration as 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
* {@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 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
* because of the way java supports enums.
@ -47,6 +47,7 @@ public class App {
*/
public static void main(String[] args) {
// 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));
LOGGER.info("DWAR={}", Nazgul.getInstance(NazgulName.DWAR));
@ -58,6 +59,7 @@ public class App {
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);

View File

@ -48,5 +48,4 @@ public class NazgulTest {
assertEquals(name, nazgul.getName());
}
}
}