add new package
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
### Relevant Articles:
|
||||
- [Memento Design Pattern in Java](https://www.baeldung.com/java-memento-design-pattern)
|
||||
- [Difference Between Fluent Interface and Builder Pattern in Java](https://www.baeldung.com/java-fluent-interface-vs-builder-pattern)
|
||||
- [Smart Batching in Java](https://www.baeldung.com/java-smart-batching)
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>design-patterns-behavioral-2</artifactId>
|
||||
<version>1.0</version>
|
||||
<name>design-patterns-behavioral-2</name>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>com.baeldung</groupId>
|
||||
<artifactId>patterns-modules</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
</project>
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.baeldung.fluentinterface;
|
||||
|
||||
import static java.util.Arrays.stream;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
public class HtmlDocument {
|
||||
private final String content;
|
||||
|
||||
private HtmlDocument(String html) {
|
||||
this.content = html;
|
||||
}
|
||||
|
||||
public HtmlDocument() {
|
||||
this("");
|
||||
}
|
||||
|
||||
public String html() {
|
||||
return String.format("<html>%s</html>", content);
|
||||
}
|
||||
|
||||
public HtmlDocument header(String header) {
|
||||
return new HtmlDocument(String.format("%s <h1>%s</h1>", content, header));
|
||||
}
|
||||
|
||||
public HtmlDocument secondaryHeader(String header) {
|
||||
return new HtmlDocument(String.format("%s <h2>%s</h2>", content, header));
|
||||
}
|
||||
|
||||
public HtmlDocument paragraph(String paragraph) {
|
||||
return new HtmlDocument(String.format("%s <p>%s</p>", content, paragraph));
|
||||
}
|
||||
|
||||
public HtmlDocument horizontalLine() {
|
||||
return new HtmlDocument(String.format("%s <hr>", content));
|
||||
}
|
||||
|
||||
public HtmlDocument orderedList(String... items) {
|
||||
String listItems = stream(items).map(el -> String.format("<li>%s</li>", el)).collect(joining());
|
||||
return new HtmlDocument(String.format("%s <ol>%s</ol>", content, listItems));
|
||||
}
|
||||
|
||||
public HtmlDocument unorderedList(String... items) {
|
||||
String listItems = stream(items).map(el -> String.format("<li>%s</li>", el)).collect(joining());
|
||||
return new HtmlDocument(String.format("%s <ul>%s</ul>", content, listItems));
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package com.baeldung.fluentinterface;
|
||||
|
||||
import com.baeldung.fluentinterface.components.HtmlElement;
|
||||
import com.baeldung.fluentinterface.components.HtmlHeader;
|
||||
import com.baeldung.fluentinterface.components.HtmlList;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.util.Arrays.stream;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
public class LargeHtmlDocument {
|
||||
private final String content;
|
||||
|
||||
private LargeHtmlDocument(String html) {
|
||||
this.content = html;
|
||||
}
|
||||
|
||||
public LargeHtmlDocument() {
|
||||
this("");
|
||||
}
|
||||
|
||||
public String html() {
|
||||
return format("<html>%s</html>", content);
|
||||
}
|
||||
|
||||
public LargeHtmlDocument head(HtmlElement head) {
|
||||
return new LargeHtmlDocument(format("%s <head>%s</head>", content, head.html()));
|
||||
}
|
||||
public LargeHtmlDocument body(HtmlElement body) {
|
||||
return new LargeHtmlDocument(format("%s <body>%s</body>", content, body.html()));
|
||||
}
|
||||
public LargeHtmlDocument footer(HtmlElement footer) {
|
||||
return new LargeHtmlDocument(format("%s <footer>%s</footer>", content, footer.html()));
|
||||
}
|
||||
|
||||
private LargeHtmlDocument append(String html) {
|
||||
return new LargeHtmlDocument(format("%s %s", content, html));
|
||||
}
|
||||
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package com.baeldung.fluentinterface;
|
||||
|
||||
public class User {
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private String email;
|
||||
private String username;
|
||||
private Long id;
|
||||
|
||||
public String name() {
|
||||
return firstName + " " + lastName;
|
||||
}
|
||||
|
||||
public User(String firstName, String lastName, String email, String username, Long id) {
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
this.email = email;
|
||||
this.username = username;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private String email;
|
||||
private String username;
|
||||
private Long id;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
public Builder firstName(String firstName) {
|
||||
this.firstName = firstName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder lastName(String lastName) {
|
||||
this.lastName = lastName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder email(String email) {
|
||||
this.email = email;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder username(String username) {
|
||||
this.username = username;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder id(Long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public User build() {
|
||||
return new User(firstName, lastName, email, username, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.baeldung.fluentinterface.components;
|
||||
|
||||
public class HorizontalLine implements HtmlElement {
|
||||
@Override
|
||||
public String html() {
|
||||
return "<hr>";
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.baeldung.fluentinterface.components;
|
||||
|
||||
public class HtmlDiv implements HtmlElement {
|
||||
|
||||
private final String content;
|
||||
|
||||
public HtmlDiv(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public HtmlDiv() {
|
||||
this.content = "";
|
||||
}
|
||||
|
||||
public HtmlDiv append(HtmlElement element) {
|
||||
return new HtmlDiv(content + element.html());
|
||||
}
|
||||
public HtmlDiv text(String text) {
|
||||
return new HtmlDiv(content + text);
|
||||
}
|
||||
public HtmlDiv paragraph(String text) {
|
||||
return new HtmlDiv(content + text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String html() {
|
||||
return String.format("<div>%s</div>", content);
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package com.baeldung.fluentinterface.components;
|
||||
|
||||
public interface HtmlElement {
|
||||
String html();
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.baeldung.fluentinterface.components;
|
||||
|
||||
public class HtmlHeader implements HtmlElement {
|
||||
|
||||
private final Type type;
|
||||
private final String value;
|
||||
|
||||
public HtmlHeader(Type type, String value) {
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String html() {
|
||||
return String.format("<%s>%s</%s>", type.tag(), value, type.tag());
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
PRIMARY("h1"),
|
||||
SECONDARY("h2"),
|
||||
THIRD("h3"),
|
||||
FOURTH("h4");
|
||||
|
||||
private final String tag;
|
||||
|
||||
Type(String tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public String tag() {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.baeldung.fluentinterface.components;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
public class HtmlList implements HtmlElement {
|
||||
|
||||
private final Type type;
|
||||
private final List<String> items;
|
||||
|
||||
public HtmlList(Type type, String... items) {
|
||||
this.type = type;
|
||||
this.items = Arrays.asList(items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String html() {
|
||||
String listItems = items.stream().map(el -> format("<li>%s</li>", el)).collect(joining());
|
||||
return String.format("<%s>%s</%s>", type.tag(), listItems, type.tag());
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
ORDERED("ol"),
|
||||
UNORDERED("ul");
|
||||
|
||||
private final String tag;
|
||||
|
||||
Type(String tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public String tag() {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.baeldung.fluentinterface.components;
|
||||
|
||||
public class HtmlSpan implements HtmlElement {
|
||||
|
||||
private final String content;
|
||||
|
||||
public HtmlSpan(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public HtmlSpan() {
|
||||
this.content = "";
|
||||
}
|
||||
|
||||
public HtmlSpan append(HtmlElement element) {
|
||||
return new HtmlSpan(content + element.html());
|
||||
}
|
||||
|
||||
public HtmlSpan paragraph(String text) {
|
||||
return new HtmlSpan(content + text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String html() {
|
||||
return String.format("<span>%s</span>", content);
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package com.baeldung.smartbatching;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
/**
|
||||
* @author KPentaris
|
||||
* @date 07/06/2023
|
||||
* @project design-patterns-behavioral-2
|
||||
*/
|
||||
public class BatchingApp {
|
||||
|
||||
static void simpleProcessing() throws Exception {
|
||||
final Path testPath = Paths.get("./testio.txt");
|
||||
testPath.toFile().createNewFile();
|
||||
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(100);
|
||||
Set<Future> futures = new HashSet<>();
|
||||
for (int i = 0; i < 50000; i++) {
|
||||
futures.add(executorService.submit(() -> {
|
||||
try {
|
||||
Files.write(testPath, Collections.singleton(Thread.currentThread().getName()), StandardOpenOption.APPEND);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}));
|
||||
}
|
||||
long start = System.currentTimeMillis();
|
||||
for (Future future : futures) {
|
||||
future.get();
|
||||
}
|
||||
System.out.println("Time: " + (System.currentTimeMillis() - start));
|
||||
executorService.shutdown();
|
||||
}
|
||||
|
||||
static void batchedProcessing() throws Exception {
|
||||
final Path testPath = Paths.get("./testio.txt");
|
||||
testPath.toFile().createNewFile();
|
||||
SmartBatcher batcher = new SmartBatcher(10, strings -> {
|
||||
List<String> content = new ArrayList<>(strings);
|
||||
content.add("-----Batch Operation-----");
|
||||
try {
|
||||
Files.write(testPath, content, StandardOpenOption.APPEND);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
for (int i = 0; i < 50000; i++) {
|
||||
batcher.submit(Thread.currentThread().getName() + "-1");
|
||||
}
|
||||
long start = System.currentTimeMillis();
|
||||
while (!batcher.finished()) {
|
||||
Thread.sleep(10);
|
||||
}
|
||||
System.out.println("Time: " + (System.currentTimeMillis() - start));
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// simpleProcessing();
|
||||
batchedProcessing();
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package com.baeldung.smartbatching;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* @author KPentaris
|
||||
* @date 07/06/2023
|
||||
* @project design-patterns-behavioral-2
|
||||
*/
|
||||
public class MicroBatcher {
|
||||
Queue<String> tasksQueue = new ConcurrentLinkedQueue<>();
|
||||
Thread batchThread;
|
||||
int executionThreshold;
|
||||
int timeoutThreshold;
|
||||
boolean working = false;
|
||||
|
||||
MicroBatcher(int executionThreshold, int timeoutThreshold, Consumer<List<String>> executionLogic) {
|
||||
batchThread = new Thread(batchHandling(executionLogic));
|
||||
batchThread.setDaemon(true);
|
||||
batchThread.start();
|
||||
this.executionThreshold = executionThreshold;
|
||||
this.timeoutThreshold = timeoutThreshold;
|
||||
}
|
||||
|
||||
void submit(String task) {
|
||||
tasksQueue.add(task);
|
||||
}
|
||||
|
||||
Runnable batchHandling(Consumer<List<String>> executionLogic) {
|
||||
return () -> {
|
||||
while (!batchThread.isInterrupted()) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
while (tasksQueue.size() < executionThreshold && (System.currentTimeMillis() - startTime) < timeoutThreshold) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
return; // exit the external loop
|
||||
}
|
||||
}
|
||||
List<String> tasks = new ArrayList<>(executionThreshold);
|
||||
while (tasksQueue.size() > 0 && tasks.size() < executionThreshold) {
|
||||
tasks.add(tasksQueue.poll());
|
||||
}
|
||||
working = true;
|
||||
executionLogic.accept(tasks);
|
||||
working = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
boolean finished() {
|
||||
return tasksQueue.isEmpty() && !working;
|
||||
}
|
||||
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package com.baeldung.smartbatching;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* @author KPentaris
|
||||
* @date 07/06/2023
|
||||
* @project design-patterns-behavioral-2
|
||||
*/
|
||||
public class SmartBatcher {
|
||||
BlockingQueue<String> tasksQueue = new LinkedBlockingQueue<>();
|
||||
Thread batchThread;
|
||||
int executionThreshold;
|
||||
boolean working = false;
|
||||
|
||||
SmartBatcher(int executionThreshold, Consumer<List<String>> executionLogic) {
|
||||
batchThread = new Thread(batchHandling(executionLogic));
|
||||
batchThread.setDaemon(true);
|
||||
batchThread.start();
|
||||
this.executionThreshold = executionThreshold;
|
||||
}
|
||||
|
||||
void submit(String task) {
|
||||
tasksQueue.add(task);
|
||||
}
|
||||
|
||||
Runnable batchHandling(Consumer<List<String>> executionLogic) {
|
||||
return () -> {
|
||||
while (!batchThread.isInterrupted()) {
|
||||
List<String> tasks = new ArrayList<>(executionThreshold);
|
||||
while (tasksQueue.drainTo(tasks, executionThreshold) == 0) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
return; // exit the external loop
|
||||
}
|
||||
}
|
||||
working = true;
|
||||
executionLogic.accept(tasks);
|
||||
working = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
boolean finished() {
|
||||
return tasksQueue.isEmpty() && !working;
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
package com.baeldung.fluentinterface;
|
||||
|
||||
import com.baeldung.fluentinterface.components.*;
|
||||
import com.baeldung.fluentinterface.components.HtmlHeader.Type;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.baeldung.fluentinterface.components.HtmlList.Type.ORDERED;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class FluentInterfaceUnitTest {
|
||||
|
||||
@Test
|
||||
void givenTenNumbers_thenStreamIsProcessedCorrectly() {
|
||||
Stream<Integer> numbers = Stream.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
Stream<String> processedNumbers = numbers.distinct()
|
||||
.filter(nr -> nr % 2 == 0)
|
||||
.skip(1)
|
||||
.limit(4)
|
||||
.map(nr -> "#" + nr)
|
||||
.peek(nr -> System.out.println(nr));
|
||||
String result = processedNumbers.collect(Collectors.joining(", "));
|
||||
|
||||
assertThat(result).isEqualTo("#2, #4, #6, #8");
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenUserBuilder_thenCreateUserCorrectly() {
|
||||
User.Builder userBuilder = User.builder();
|
||||
userBuilder = userBuilder
|
||||
.firstName("John")
|
||||
.lastName("Doe")
|
||||
.email("jd@gmail.com")
|
||||
.username("jd_2000")
|
||||
.id(1234L);
|
||||
|
||||
User user = userBuilder.build();
|
||||
|
||||
assertThat(user.name()).isEqualTo("John Doe");
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenHtmlDocument_thenGenerateHtmlCorrectly() {
|
||||
HtmlDocument document = new HtmlDocument()
|
||||
.header("Principles of O.O.P.")
|
||||
.paragraph("OOP in Java.")
|
||||
.horizontalLine()
|
||||
.paragraph("The main pillars of OOP are:")
|
||||
.orderedList("Encapsulation", "Inheritance", "Abstraction", "Polymorphism");
|
||||
String html = document.html();
|
||||
|
||||
assertThat(html).isEqualToIgnoringWhitespace(
|
||||
"<html>"
|
||||
+ "<h1>Principles of O.O.P.</h1>"
|
||||
+ "<p>OOP in Java.</p>"
|
||||
+ "<hr>"
|
||||
+ "<p>The main pillars of OOP are:</p>"
|
||||
+ "<ol>"
|
||||
+ "<li>Encapsulation</li>"
|
||||
+ "<li>Inheritance</li>"
|
||||
+ "<li>Abstraction</li>"
|
||||
+ "<li>Polymorphism</li>"
|
||||
+ "</ol>"
|
||||
+ "</html>"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenHtmlDocument_thenInstanceIsImmutable() {
|
||||
HtmlDocument document = new HtmlDocument()
|
||||
.header("Principles of O.O.P.");
|
||||
HtmlDocument updatedDocument = document
|
||||
.paragraph("OOP in Java.");
|
||||
|
||||
assertThat(document).isNotEqualTo(updatedDocument);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void givenLargeHtmlDocument_thenGenerateHtmlCorrectly() {
|
||||
String html = new LargeHtmlDocument()
|
||||
.head(new HtmlHeader(Type.PRIMARY, "title"))
|
||||
.body(new HtmlDiv()
|
||||
.append(new HtmlSpan()
|
||||
.paragraph("learning OOP from John Doe")
|
||||
.append(new HorizontalLine())
|
||||
.paragraph("The pillars of OOP:")
|
||||
)
|
||||
.append(new HtmlList(ORDERED, "Encapsulation", "Inheritance", "Abstraction", "Polymorphism"))
|
||||
)
|
||||
.footer(new HtmlDiv()
|
||||
.paragraph("trademark John Doe")
|
||||
)
|
||||
.html();
|
||||
|
||||
String expectedHtml = "<html> <head><h1>title</h1></head> <body><div><span>learning OOP from John Doe<hr>The pillars of OOP:</span><ol><li>Encapsulation</li><li>Inheritance</li><li>Abstraction</li><li>Polymorphism</li></ol></div></body> <footer><div>trademark John Doe</div></footer></html>";
|
||||
assertThat(html).isEqualToIgnoringWhitespace(expectedHtml);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user