actorRegister = new ConcurrentHashMap<>();
+ private final AtomicInteger idCounter = new AtomicInteger(0);
+
+ public void startActor(Actor actor) {
+ String actorId = "actor-" + idCounter.incrementAndGet(); // Generate a new and unique ID
+ actor.setActorId(actorId); // assign the actor it's ID
+ actorRegister.put(actorId, actor); // Register and save the actor with it's ID
+ executor.submit(actor); // Run the actor in a thread
+ }
+
+ public Actor getActorById(String actorId) {
+ return actorRegister.get(actorId); // Find by Id
+ }
+
+ public void shutdown() {
+ executor.shutdownNow(); // Stop all threads
+ }
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/App.java b/actor-model/src/main/java/com/iluwatar/actormodel/App.java
new file mode 100644
index 000000000000..79fe79e48a6f
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/App.java
@@ -0,0 +1,64 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * The Actor Model is a design pattern used to handle concurrency in a safe, scalable, and
+ * message-driven way.
+ *
+ * In the Actor Model: - An **Actor** is an independent unit that has its own state and behavior.
+ * - Actors **communicate only through messages** — they do not share memory. - An **ActorSystem**
+ * is responsible for creating, starting, and managing the lifecycle of actors. - Messages are
+ * delivered asynchronously, and each actor processes them one at a time.
+ *
+ *
💡 Key benefits: - No shared memory = no need for complex thread-safety - Easy to scale with
+ * many actors - Suitable for highly concurrent or distributed systems
+ *
+ *
🔍 This example demonstrates the Actor Model: - `ActorSystem` starts two actors: `srijan` and
+ * `ansh`. - `ExampleActor` and `ExampleActor2` extend the `Actor` class and override the
+ * `onReceive()` method to handle messages. - Actors communicate using `send()` to pass `Message`
+ * objects that include the message content and sender's ID. - The actors process messages
+ * **asynchronously in separate threads**, and we allow a short delay (`Thread.sleep`) to let them
+ * run. - The system is shut down gracefully at the end.
+ */
+package com.iluwatar.actormodel;
+
+public class App {
+ public static void main(String[] args) throws InterruptedException {
+ ActorSystem system = new ActorSystem();
+ Actor srijan = new ExampleActor(system);
+ Actor ansh = new ExampleActor2(system);
+
+ system.startActor(srijan);
+ system.startActor(ansh);
+ ansh.send(new Message("Hello ansh", srijan.getActorId()));
+ srijan.send(new Message("Hello srijan!", ansh.getActorId()));
+
+ Thread.sleep(1000); // Give time for messages to process
+
+ srijan.stop(); // Stop the actor gracefully
+ ansh.stop();
+ system.shutdown(); // Stop the actor system
+ }
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor.java b/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor.java
new file mode 100644
index 000000000000..fd49325f44bd
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor.java
@@ -0,0 +1,53 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.actormodel;
+
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class ExampleActor extends Actor {
+ private final ActorSystem actorSystem;
+ @Getter private final List receivedMessages = new ArrayList<>();
+
+ public ExampleActor(ActorSystem actorSystem) {
+ this.actorSystem = actorSystem;
+ }
+
+ // Logger log = Logger.getLogger(getClass().getName());
+
+ @Override
+ protected void onReceive(Message message) {
+ LOGGER.info(
+ "[{}]Received : {} from : [{}]", getActorId(), message.getContent(), message.getSenderId());
+ Actor sender = actorSystem.getActorById(message.getSenderId()); // sender actor id
+ // Reply of the message
+ if (sender != null && !message.getSenderId().equals(getActorId())) {
+ sender.send(new Message("I got your message ", getActorId()));
+ }
+ }
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor2.java b/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor2.java
new file mode 100644
index 000000000000..037f96716558
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor2.java
@@ -0,0 +1,46 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.actormodel;
+
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class ExampleActor2 extends Actor {
+ private final ActorSystem actorSystem;
+ @Getter private final List receivedMessages = new ArrayList<>();
+
+ public ExampleActor2(ActorSystem actorSystem) {
+ this.actorSystem = actorSystem;
+ }
+
+ @Override
+ protected void onReceive(Message message) {
+ receivedMessages.add(message.getContent());
+ LOGGER.info("[{}]Received : {}", getActorId(), message.getContent());
+ }
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/Message.java b/actor-model/src/main/java/com/iluwatar/actormodel/Message.java
new file mode 100644
index 000000000000..03ca6e02cac0
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/Message.java
@@ -0,0 +1,35 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.actormodel;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor
+@Getter
+public class Message {
+ private final String content;
+ private final String senderId;
+}
diff --git a/actor-model/src/test/java/com/iluwatar/actor/ActorModelTest.java b/actor-model/src/test/java/com/iluwatar/actor/ActorModelTest.java
new file mode 100644
index 000000000000..a4a0dee569ab
--- /dev/null
+++ b/actor-model/src/test/java/com/iluwatar/actor/ActorModelTest.java
@@ -0,0 +1,63 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.iluwatar.actor;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import com.iluwatar.actormodel.ActorSystem;
+import com.iluwatar.actormodel.App;
+import com.iluwatar.actormodel.ExampleActor;
+import com.iluwatar.actormodel.ExampleActor2;
+import com.iluwatar.actormodel.Message;
+import org.junit.jupiter.api.Test;
+
+public class ActorModelTest {
+ @Test
+ void testMainMethod() throws InterruptedException {
+ App.main(new String[] {});
+ }
+
+ @Test
+ public void testMessagePassing() throws InterruptedException {
+ ActorSystem system = new ActorSystem();
+
+ ExampleActor srijan = new ExampleActor(system);
+ ExampleActor2 ansh = new ExampleActor2(system);
+
+ system.startActor(srijan);
+ system.startActor(ansh);
+
+ // Ansh recieves a message from Srijan
+ ansh.send(new Message("Hello ansh", srijan.getActorId()));
+
+ // Wait briefly to allow async processing
+ Thread.sleep(200);
+
+ // Check that Srijan received the message
+ assertTrue(
+ ansh.getReceivedMessages().contains("Hello ansh"),
+ "ansh should receive the message from Srijan");
+ }
+}
diff --git a/data-access-object/README.md b/data-access-object/README.md
index bd020252d253..7e84299e23e1 100644
--- a/data-access-object/README.md
+++ b/data-access-object/README.md
@@ -199,10 +199,6 @@ The program output:
10:02:09.898 [main] INFO com.iluwatar.dao.App -- customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@f2f2cc1
```
-## Detailed Explanation of Data Access Object Pattern with Real-World Examples
-
-
-
## When to Use the Data Access Object Pattern in Java
Use the Data Access Object in any of the following situations:
diff --git a/data-locality/README.md b/data-locality/README.md
index e9f556b8ad5c..a59103b3f89b 100644
--- a/data-locality/README.md
+++ b/data-locality/README.md
@@ -128,10 +128,6 @@ The console output:
In this way, the data-locality module demonstrates the Data Locality pattern. By updating all components of the same type together, it increases the likelihood that the data needed for the update is already in the cache, thereby improving performance.
-## Detailed Explanation of Data Locality Pattern with Real-World Examples
-
-
-
## When to Use the Data Locality Pattern in Java
This pattern is applicable in scenarios where large datasets are processed and performance is critical. It's particularly useful in:
diff --git a/dynamic-proxy/pom.xml b/dynamic-proxy/pom.xml
index 236723c52682..586dbc1b6ab4 100644
--- a/dynamic-proxy/pom.xml
+++ b/dynamic-proxy/pom.xml
@@ -56,7 +56,7 @@
org.springframework
spring-web
- 7.0.0-M3
+ 7.0.0-M4
org.junit.jupiter
diff --git a/localization/fa/abstract-document/README.md b/localization/fa/abstract-document/README.md
new file mode 100644
index 000000000000..7097ffc8b4ea
--- /dev/null
+++ b/localization/fa/abstract-document/README.md
@@ -0,0 +1,243 @@
+---
+title: "الگوی Abstract Document در جاوا: سادهسازی مدیریت داده با انعطافپذیری"
+shortTitle: Abstract Document
+description: "الگوی طراحی Abstract Document در جاوا را بررسی کنید. با هدف، توضیح، کاربرد، مزایا و نمونههای دنیای واقعی برای پیادهسازی ساختارهای دادهای پویا و انعطافپذیر آشنا شوید."
+category: Structural
+language: fa
+tag:
+ - Abstraction
+ - Decoupling
+ - Dynamic typing
+ - Encapsulation
+ - Extensibility
+ - Polymorphism
+---
+
+## هدف الگوی طراحی Abstract Document
+
+الگوی طراحی Abstract Document در جاوا یک الگوی طراحی ساختاری مهم است که راهی یکپارچه برای مدیریت ساختارهای دادهای سلسلهمراتبی و درختی فراهم میکند، با تعریف یک واسط مشترک برای انواع مختلف اسناد. این الگو ساختار اصلی سند را از فرمتهای خاص داده جدا میکند، که باعث بهروزرسانی پویا و نگهداری سادهتر میشود.
+
+## توضیح دقیق الگوی Abstract Document با نمونههای دنیای واقعی
+
+الگوی طراحی Abstract Document در جاوا امکان مدیریت پویا ویژگیهای پویا(غیر استاتیک) را فراهم میکند. این الگو از مفهوم traits استفاده میکند تا ایمنی نوعداده (type safety) را فراهم کرده و ویژگیهای کلاسهای مختلف را به مجموعهای از واسطها تفکیک کند.
+
+مثال دنیای واقعی
+
+> فرض کنید یک سیستم کتابخانه از الگوی Abstract Document در جاوا استفاده میکند، جایی که کتابها میتوانند فرمتها و ویژگیهای متنوعی داشته باشند: کتابهای فیزیکی، کتابهای الکترونیکی، و کتابهای صوتی. هر فرمت ویژگیهای خاص خود را دارد، مانند تعداد صفحات برای کتابهای فیزیکی، حجم فایل برای کتابهای الکترونیکی، و مدتزمان برای کتابهای صوتی. الگوی Abstract Document به سیستم کتابخانه اجازه میدهد تا این فرمتهای متنوع را بهصورت انعطافپذیر مدیریت کند. با استفاده از این الگو، سیستم میتواند ویژگیها را بهصورت پویا ذخیره و بازیابی کند، بدون نیاز به ساختار سفت و سخت برای هر نوع کتاب، و این کار افزودن فرمتها یا ویژگیهای جدید را در آینده بدون تغییرات عمده در کد آسان میسازد.
+
+به زبان ساده
+
+> الگوی Abstract Document اجازه میدهد ویژگیهایی به اشیاء متصل شوند بدون اینکه خود آن اشیاء از آن اطلاع داشته باشند.
+
+ویکیپدیا میگوید
+
+> یک الگوی طراحی ساختاری شیءگرا برای سازماندهی اشیاء در کلید-مقدارهایی با تایپ آزاد و ارائه دادهها از طریق نمای تایپ است. هدف این الگو دستیابی به انعطافپذیری بالا بین اجزا در یک زبان strongly typed است که در آن بتوان ویژگیهای جدیدی را بهصورت پویا به ساختار درختی اشیاء اضافه کرد، بدون از دست دادن پشتیبانی از type safety. این الگو از traits برای جداسازی ویژگیهای مختلف یک کلاس در اینترفیسهای متفاوت استفاده میکند.
+
+نمودار کلاس
+
+
+
+## مثال برنامهنویسی از الگوی Abstract Document در جاوا
+
+فرض کنید یک خودرو داریم که از قطعات مختلفی تشکیل شده است. اما نمیدانیم آیا این خودرو خاص واقعاً همه قطعات را دارد یا فقط برخی از آنها. خودروهای ما پویا و بسیار انعطافپذیر هستند.
+
+بیایید ابتدا کلاسهای پایه `Document` و `AbstractDocument` را تعریف کنیم. این کلاسها اساساً یک شیء را قادر میسازند تا یک نقشه از ویژگیها و هر تعداد شیء فرزند را نگه دارد.
+
+```java
+public interface Document {
+
+ Void put(String key, Object value);
+
+ Object get(String key);
+
+ Stream children(String key, Function
-
- org.junit.jupiter
- junit-jupiter-params
- ${junit.version}
- test
-
org.junit.jupiter
junit-jupiter-migrationsupport
@@ -481,7 +478,7 @@
com.diffplug.spotless
spotless-maven-plugin
- 2.44.3
+ 2.44.4
diff --git a/publish-subscribe/README.md b/publish-subscribe/README.md
index 8ff29f2c6a0f..86f6c53166f9 100644
--- a/publish-subscribe/README.md
+++ b/publish-subscribe/README.md
@@ -1,97 +1,101 @@
---
-title: "Publish-Subscribe Pattern in Java: Decoupling the solution with asynchronous communication"
+title: "Publish-Subscribe Pattern in Java: Decoupling components with asynchronous communication"
shortTitle: Publish-Subscribe
description: "Explore the Publish-Subscribe design pattern in Java with detailed examples. Learn how it helps to create loosely coupled, scalable, and flexible systems by allowing components to communicate asynchronously without knowing each other directly."
-category: Behavioral
+category: Messaging
language: en
tag:
+ - Architecture
+ - Asynchronous
- Decoupling
+ - Event-driven
+ - Messaging
+ - Microservices
+ - Publish/subscribe
+ - Scalability
---
## Intent of the Publish-Subscribe Design Pattern
-The Publish-Subscribe design pattern is widely used in software architecture to transmit data between various components in a system.
-It is a behavioral design pattern aimed at achieving loosely coupled communication between objects.
-The primary intent is to allow a one-to-many dependency relationship where one object (the Publisher) notifies multiple other objects (the Subscribers)
-about changes or events, without needing to know who or what the subscribers are.
+Defines a one-to-many dependency between objects, enabling automatic notification of multiple subscribers when a publisher's state changes or an event occurs.
## Detailed Explanation of Publish-Subscribe Pattern with Real-World Examples
-### Real-world example
+Real-world example
-- Messaging systems like Kafka, RabbitMQ, AWS SNS, JMS
- - **Kafka** : publishes messages to topics and subscribers consumes them in real time for analytics, logs or other purposes.
- - **RabbitMQ** : Uses exchanges as publisher and queues as subscribers to route messages
- - **AWS SNS** : Simple Notification Service (SNS) received the messages from publishers with topic and the subscribers on that topic will receive the messages. (SQS, Lambda functions, emails, SMS)
+> An analogous real-world example of the Publish-Subscribe pattern is a news broadcasting system. A news agency (publisher) broadcasts breaking news stories without knowing who specifically receives them. Subscribers, such as television stations, news websites, or mobile news apps, independently decide which types of news they want to receive (e.g., sports, politics, weather) and are automatically notified whenever relevant events occur. This approach keeps the news agency unaware of subscribers' specifics, allowing flexible and scalable distribution of information.
+In plain words
-- Event driven microservices
- - **Publisher** : Point of Sale(PoS) system records the sale of an item and publish the event
- - **Subscribers** : Inventory management service updates stock, Billing service sends e-bill to customer
+> The Publish-Subscribe design pattern allows senders (publishers) to broadcast messages to multiple receivers (subscribers) without knowing who they are, enabling loose coupling and asynchronous communication in a system.
+Wikipedia says
-- Newsletter subscriptions
- - **Publisher** : Writes a new blog post and publish to subscribers
- - **Subscribers** : All the subscribers to the newsletter receive the email
-
-### In plain words
-
-The Publish-Subscribe design pattern allows senders (publishers) to broadcast messages to multiple receivers (subscribers) without knowing who they are,
-enabling loose coupling and asynchronous communication in a system
-
-### Wikipedia says
-
-In software architecture, publish–subscribe or pub/sub is a messaging pattern where publishers categorize messages into classes that are received by subscribers.
-This is contrasted to the typical messaging pattern model where publishers send messages directly to subscribers.
-
-Similarly, subscribers express interest in one or more classes and only receive messages that are of interest, without knowledge of which publishers, if any, there are.
-
-Publish–subscribe is a sibling of the message queue paradigm, and is typically one part of a larger message-oriented middleware system.
+> In software architecture, publish–subscribe or pub/sub is a messaging pattern where publishers categorize messages into classes that are received by subscribers.
+This is contrasted to the typical messaging pattern model where publishers send messages directly to subscribers. Similarly, subscribers express interest in one or more classes and only receive messages that are of interest, without knowledge of which publishers, if any, there are. Publish–subscribe is a sibling of the message queue paradigm, and is typically one part of a larger message-oriented middleware system.
Most messaging systems support both the pub/sub and message queue models in their API; e.g., Java Message Service (JMS).
-### Architectural Diagram
-
+Sequence diagram
+
+
## Programmatic Example of Publish-Subscribe Pattern in Java
-First we need to identify the Event on which we need the pub-sub methods to trigger.
-For example:
+First, we identify events that trigger the publisher-subscriber interactions. Common examples include:
+
+* Sending alerts based on weather events, like earthquakes, floods, and tornadoes.
+* Sending notifications based on temperature changes.
+* Sending emails to customer support when support tickets are created.
-- Sending alerts based on the weather events such as earthquakes, floods and tornadoes
-- Sending alerts based on the temperature
-- Sending an email to different customer support emails when a support ticket is created.
+### Defining the Message
-The Message class below will hold the content of the message we need to pass between the publisher and the subscribers.
+We start with a simple message class encapsulating the information sent from publishers to subscribers.
```java
public record Message(Object content) {
}
-
```
-The Topic class will have the topic **name** based on the event
+### Defining Topics
-- Weather events TopicName WEATHER
-- Weather events TopicName TEMPERATURE
-- Support ticket created TopicName CUSTOMER_SUPPORT
-- Any other custom topic depending on use case
-- Also, the Topic contains a list of subscribers that will listen to that topic
+A Topic represents an event category that subscribers can register to and publishers can publish messages to. Each topic has:
-We can add or remove subscribers from the subscription to the topic
+* A unique identifier or name (e.g., WEATHER, TEMPERATURE, CUSTOMER_SUPPORT).
+* A collection of subscribers listening to this topic.
+
+Subscribers can dynamically subscribe or unsubscribe.
```java
+@Getter
+@Setter
+@RequiredArgsConstructor
public class Topic {
- private final TopicName name;
- private final Set subscribers = new CopyOnWriteArraySet<>();
- //...//
+ private final String topicName;
+ private final Set subscribers = new CopyOnWriteArraySet<>();
+
+ public void addSubscriber(Subscriber subscriber) {
+ subscribers.add(subscriber);
+ }
+
+ public void removeSubscriber(Subscriber subscriber) {
+ subscribers.remove(subscriber);
+ }
+
+ public void publish(Message message) {
+ for (Subscriber subscriber : subscribers) {
+ CompletableFuture.runAsync(() -> subscriber.onMessage(message));
+ }
+ }
}
```
-Then we can create the publisher. The publisher class has a set of topics.
+### Publisher Implementation
+
+The Publisher maintains a collection of topics it can publish to.
-- Each new topic has to be registered in the publisher.
-- Publish method will publish the _Message_ to the corresponding _Topic_.
+* Before publishing, a topic must be registered.
+* Upon publishing, it forwards messages to subscribers of the corresponding topic.
```java
public class PublisherImpl implements Publisher {
@@ -115,20 +119,12 @@ public class PublisherImpl implements Publisher {
}
```
-Finally, we can Subscribers to the Topics we want to listen to.
+### Defining Subscribers
-- For WEATHER topic we will create _WeatherSubscriber_
-- _WeatherSubscriber_ can also subscribe to TEMPERATURE topic
-- For CUSTOMER_SUPPORT topic we will create _CustomerSupportSubscribe_
-- Also to demonstrate the async behavior we will create a _DelayedWeatherSubscriber_ who has a 0.2 sec processing deplay
+Subscribers implement an interface that handles incoming messages.
-All classes will have a _onMessage_ method which will take a Message input.
-
-- On message method will verify the content of the message is as expected
-- After content is verified it will perform the operation based on the message
- - _WeatherSubscriber_ will send a weather or temperature alert based on the _Message_
- - _CustomerSupportSubscribe_will send an email based on the _Message_
- - _DelayedWeatherSubscriber_ will send a weather alert based on the _Message_ after a delay
+* Each subscriber processes messages according to specific logic.
+* Subscribers can be registered to multiple topics.
```java
public interface Subscriber {
@@ -136,7 +132,21 @@ public interface Subscriber {
}
```
-And here is the invocation of the publisher and subscribers.
+Subscriber examples:
+
+* WeatherSubscriber: handles alerts for weather events or temperature changes.
+* CustomerSupportSubscriber: handles support tickets by sending emails.
+* DelayedWeatherSubscriber: simulates delayed processing for demonstrating asynchronous behavior.
+
+### Example Usage (Invocation)
+
+Here's how all components connect:
+
+1. Create Publisher
+2. Register Topics with Publisher
+3. Create Subscribers and Subscribe to Relevant Topics
+4. Publish Messages
+5. Manage Subscriptions Dynamically
```java
public static void main(String[] args) throws InterruptedException {
@@ -205,10 +215,9 @@ public static void main(String[] args) throws InterruptedException {
}
```
-Program output:
+### Program output
-Note that the order of output could change everytime you run the program.
-The subscribers could take different time to consume the message.
+Output may vary due to asynchronous subscriber processing:
```
14:01:45.599 [ForkJoinPool.commonPool-worker-6] INFO com.iluwatar.publish.subscribe.subscriber.CustomerSupportSubscriber -- Customer Support Subscriber: 1416331388 sent the email to: support@test.de
@@ -219,81 +228,48 @@ The subscribers could take different time to consume the message.
14:01:47.600 [ForkJoinPool.commonPool-worker-3] INFO com.iluwatar.publish.subscribe.subscriber.DelayedWeatherSubscriber -- Delayed Weather Subscriber: 2085808749 issued message: earthquake
```
-## When to Use the Publish-Subscribe Pattern
+This demonstrates:
-- Event-Driven Systems
- - Use Pub/Sub when your system relies on events (e.g., user registration, payment completion).
- - Example: After a user registers, send a welcome email and log the action simultaneously.
+* Subscribers reacting independently to messages published to subscribed topics.
+* Dynamic subscription management allows changing which subscribers listen to specific topics.
+* The asynchronous and loosely coupled nature of the publish-subscribe pattern in Java applications.
-- Asynchronous Communication
- - When tasks can be performed without waiting for immediate responses.
- - Example: In an e-commerce app, notify the warehouse and the user after a successful order.
+## When to Use the Publish-Subscribe Pattern
-- Decoupling Components
- - Ideal for systems where producers and consumers should not depend on each other.
- - Example: A logging service listens for logs from multiple microservices.
+* When an application requires loose coupling between event producers and consumers.
+* In scenarios where multiple subscribers independently react to the same event.
+* When developing scalable, asynchronous messaging systems, particularly within microservices architectures.
-- Scaling Systems
- - Useful when you need to scale services without changing the core application logic.
- - Example: Broadcasting messages to thousands of clients (chat applications, IoT).
+## Real-World Applications of Publish-Subscribe Pattern in Java
-- Broadcasting Notifications
- - When a message should be delivered to multiple receivers.
- - Example: Sending promotional offers to multiple user devices.
+* Java Message Service (JMS) implementations (ActiveMQ, RabbitMQ)
+* Apache Kafka (used extensively in Java-based microservices)
+* Spring Framework's event publishing and listening mechanisms
+* Google Cloud Pub/Sub in Java applications
+* AWS Simple Notification Service (SNS) with Java SDK
-- Microservices Communication
- - Allow independent services to communicate without direct coupling.
- - Example: An order service publishes an event, and both the billing and shipping services process it.
+## Benefits and Trade-offs of Publish-Subscribe Pattern
-## When to avoid the Publish-Subscribe Pattern
+Benefits:
-- Simple applications where direct calls suffice.
-- Strong consistency requirements (e.g., banking transactions).
-- Low-latency synchronous communication needed.
+* Loose coupling between publishers and subscribers promotes flexibility.
+* Improved scalability and maintainability as new subscribers can be easily added.
+* Supports asynchronous communication, enhancing system responsiveness.
-## Benefits and Trade-offs of Publish-Subscribe Pattern
+Trade-offs:
-### Benefits:
-
-- Decoupling
- - Publishers and subscribers are independent of each other.
- - Publishers don’t need to know who the subscribers are, and vice versa.
- - Changes in one component don’t affect the other.
-- Scalability
- - New subscribers can be added without modifying publishers.
- - Supports distributed systems where multiple services consume the same events.
-- Dynamic Subscription
- - Subscribers can subscribe/unsubscribe at runtime.
- - Enables flexible event-driven architectures.
-- Asynchronous Communication
- - Publishers and subscribers operate independently, improving performance.
- - Useful for background processing (e.g., notifications, logging).
-- Broadcast Communication
- - A single event can be consumed by multiple subscribers.
- - Useful for fan-out scenarios (e.g., notifications, analytics).
-- Resilience & Fault Tolerance
- - If a subscriber fails, others can still process messages.
- - Message brokers (e.g., Kafka, RabbitMQ) can retry or persist undelivered messages.
-
-### Trade-offs:
-
-- Complexity in Debugging
- - Since publishers and subscribers are decoupled, tracing event flow can be difficult.
- - Requires proper logging and monitoring tools.
-- Message Ordering & Consistency
- - Ensuring message order across subscribers can be challenging (e.g., Kafka vs. RabbitMQ).
- - Some systems may process events out of order.
-- Potential Latency
- - Asynchronous processing introduces delays compared to direct calls.
- - Not ideal for real-time synchronous requirements.
+* Increased complexity due to asynchronous message handling and debugging difficulties.
+* Potential message delivery delays and inconsistency if the infrastructure isn't reliable.
+* Risk of message flooding, requiring proper infrastructure and consumer management.
## Related Java Design Patterns
-* [Observer Pattern](https://github.com/sanurah/java-design-patterns/blob/master/observer/): Both involve a producer (subject/publisher) notifying consumers (observers/subscribers). Observer is synchronous & tightly coupled (observers know the subject). Pub-Sub is asynchronous & decoupled (via a message broker).
-* [Mediator Pattern](https://github.com/sanurah/java-design-patterns/blob/master/mediator/): A mediator centralizes communication between components (like a message broker in Pub-Sub). Mediator focuses on reducing direct dependencies between objects. Pub-Sub focuses on broadcasting events to unknown subscribers.
+* [Observer Pattern](https://java-design-patterns.com/patterns/observer/): Both patterns establish a publisher-subscriber relationship; however, Observer typically works within a single application boundary synchronously, whereas Publish-Subscribe is often distributed and asynchronous.
+* [Mediator Pattern](https://java-design-patterns.com/patterns/mediator/): Mediator encapsulates interactions between objects in a centralized manner, whereas Publish-Subscribe provides decentralized, loosely-coupled interactions.
## References and Credits
-* [Apache Kafka – Pub-Sub Model](https://kafka.apache.org/documentation/#design_pubsub)
-* [Microsoft – Publish-Subscribe Pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/publisher-subscriber)
-* [Martin Fowler – Event-Driven Architecture](https://martinfowler.com/articles/201701-event-driven.html)
+* [Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions](https://amzn.to/3WcFVui)
+* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
+* [Pattern-Oriented Software Architecture Volume 2: Patterns for Concurrent and Networked Objects](https://amzn.to/3UgC24V)
+* [Publisher-Subscriber Pattern (Microsoft)](https://learn.microsoft.com/en-us/azure/architecture/patterns/publisher-subscriber)
diff --git a/publish-subscribe/etc/publish-subscribe-sequence-diagram.png b/publish-subscribe/etc/publish-subscribe-sequence-diagram.png
new file mode 100644
index 000000000000..8fb1213417b5
Binary files /dev/null and b/publish-subscribe/etc/publish-subscribe-sequence-diagram.png differ
diff --git a/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/AppTest.java b/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/AppTest.java
index 8080553078f2..50c780cb682d 100644
--- a/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/AppTest.java
+++ b/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/AppTest.java
@@ -28,7 +28,7 @@
import org.junit.jupiter.api.Test;
-public class AppTest {
+class AppTest {
@Test
void shouldExecuteApplicationWithoutException() {
diff --git a/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/LoggerExtension.java b/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/LoggerExtension.java
index 05e16e5a1110..ecf015752e48 100644
--- a/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/LoggerExtension.java
+++ b/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/LoggerExtension.java
@@ -28,7 +28,6 @@
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import java.util.List;
-import java.util.stream.Collectors;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
@@ -53,12 +52,10 @@ public void beforeEach(ExtensionContext extensionContext) throws Exception {
}
public List getMessages() {
- return listAppender.list.stream().map(e -> e.getMessage()).collect(Collectors.toList());
+ return listAppender.list.stream().map(e -> e.getMessage()).toList();
}
public List getFormattedMessages() {
- return listAppender.list.stream()
- .map(e -> e.getFormattedMessage())
- .collect(Collectors.toList());
+ return listAppender.list.stream().map(e -> e.getFormattedMessage()).toList();
}
}
diff --git a/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/model/MessageTest.java b/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/model/MessageTest.java
index a08624a3fab2..636e1ed66c0f 100644
--- a/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/model/MessageTest.java
+++ b/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/model/MessageTest.java
@@ -29,10 +29,10 @@
import org.junit.jupiter.api.Test;
-public class MessageTest {
+class MessageTest {
@Test
- public void testMessage() {
+ void testMessage() {
final String content = "some content";
Message message = new Message(content);
assertInstanceOf(String.class, message.content());
diff --git a/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/model/TopicTest.java b/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/model/TopicTest.java
index eb2d87c8c127..cbb5a9882e71 100644
--- a/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/model/TopicTest.java
+++ b/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/model/TopicTest.java
@@ -33,7 +33,7 @@
import java.util.Set;
import org.junit.jupiter.api.Test;
-public class TopicTest {
+class TopicTest {
private static final String TOPIC_WEATHER = "WEATHER";
diff --git a/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/publisher/PublisherTest.java b/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/publisher/PublisherTest.java
index d3db88c421bf..7105db20fa93 100644
--- a/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/publisher/PublisherTest.java
+++ b/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/publisher/PublisherTest.java
@@ -36,7 +36,7 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
-public class PublisherTest {
+class PublisherTest {
@RegisterExtension public LoggerExtension loggerExtension = new LoggerExtension();
diff --git a/service-stub/README.md b/service-stub/README.md
index 980e3561d697..5fa4533587ea 100644
--- a/service-stub/README.md
+++ b/service-stub/README.md
@@ -2,26 +2,30 @@
title: "Service Stub Pattern in Java: Simplifying Testing with Stub Implementations"
shortTitle: Service Stub
description: "Explore the Service Stub design pattern in Java using a Sentiment Analysis example. Learn how stub implementations provide dummy services to facilitate testing and development."
-category: Structural
+category: Testing
language: en
tag:
+ - API design
- Decoupling
+ - Integration
+ - Microservices
+ - Testing
---
## Also known as
-* Dummy Service
-* Fake Service
+* Service Mock
+* Test Double
## Intent of Service Stub Pattern
-The Service Stub pattern provides a lightweight, dummy implementation of an external service to allow testing or development without relying on the real service, which may be unavailable, slow, or resource-intensive.
+Provide a lightweight, simplified implementation of a remote or external service to facilitate testing in isolation.
## Detailed Explanation of Service Stub Pattern with Real-World Example
Real-world example
-> In this example, we simulate a **Sentiment Analysis Service**. The real implementation delays execution and randomly decides the sentiment. The stub implementation, on the other hand, quickly returns predefined responses based on input text ("good", "bad", or neutral), making it ideal for testing.
+> A real-world analogy for the Service Stub pattern could be a flight simulator used to train pilots. Instead of learning to fly directly using a real airplane—which would be costly, dangerous, and often impractical—pilots initially practice within a simulator. This simulator provides predefined, realistic scenarios and reactions, enabling pilots to train safely, repeatedly, and predictably without the complexities and risks associated with actual flight operations. Similarly, a Service Stub provides controlled, predictable responses for external services during testing, simplifying and accelerating software development and testing processes.
In plain words
@@ -37,21 +41,24 @@ Sequence diagram
## Programmatic Example of Service Stub Pattern in Java
-We define a `SentimentAnalysisService` interface and provide two implementations:
+We demonstrate the Service Stub pattern using a simple sentiment analysis example. To illustrate this clearly, we define a common interface `SentimentAnalysisServer` and create two separate implementations:
-1. **RealSentimentAnalysisServer**: Simulates a slow, random sentiment analysis system.
-2. **StubSentimentAnalysisServer**: Returns a deterministic result based on input keywords.
+**RealSentimentAnalysisServer**: Represents a slow, realistic sentiment analysis service, returning random sentiment results to simulate external complexity and latency.
+
+**StubSentimentAnalysisServer**: Provides fast, deterministic results based on simple keyword matching, suitable for isolated testing without external dependencies.
+
+### Step-by-step Example Implementation
+
+First, define a common interface that both implementations will use:
-### Example Implementation
-Both the real service and the stub implement the interface below.
```java
public interface SentimentAnalysisServer {
String analyzeSentiment(String text);
}
```
-The real sentiment analysis class returns a random response for a given input and simulates the runtime by sleeping
-the Thread for 5 seconds. The Supplier\ allows injecting controlled sentiment values during testing, ensuring
-deterministic outputs.
+
+Next, we create a realistic implementation that simulates a slow, external service. It introduces a delay of 5 seconds and returns random sentiment results (`Positive`, `Negative`, or `Neutral`). For flexibility and easier testing, it allows injecting a custom sentiment supplier:
+
```java
public class RealSentimentAnalysisServer implements SentimentAnalysisServer {
@@ -77,8 +84,9 @@ public class RealSentimentAnalysisServer implements SentimentAnalysisServer {
}
}
```
-The stub implementation simulates the real sentiment analysis class and provides a deterministic output
-for a given input. Additionally, its runtime is almost zero.
+
+Then, we provide a simplified stub implementation designed specifically for testing purposes. It returns immediate and predictable results based on simple keyword detection. This enables tests to run quickly and consistently without relying on external factors:
+
```java
public class StubSentimentAnalysisServer implements SentimentAnalysisServer {
@@ -95,9 +103,10 @@ public class StubSentimentAnalysisServer implements SentimentAnalysisServer {
}
}
}
-
```
-Here is the main function of the App class (entry point to the program)
+
+Finally, here's the main application logic illustrating how to use both implementations in practice. Notice the significant performance difference between the real and stub implementations:
+
```java
@Slf4j
public static void main(String[] args) {
@@ -116,39 +125,46 @@ Here is the main function of the App class (entry point to the program)
LOGGER.info("The sentiment is: {}", sentiment);
}
```
-## When to Use the Service Stub Pattern in Java
-Use the Service Stub pattern when:
+In summary, implementing a Service Stub involves creating a simplified substitute (`StubSentimentAnalysisServer`) for an actual external service (`RealSentimentAnalysisServer`). This approach allows your tests to run quickly and consistently by isolating them from external complexity and unpredictability.
+
+## When to Use the Service Stub Pattern in Java
-* Testing components that depend on external services.
-* The real service is slow, unreliable, or unavailable.
-* You need predictable, predefined responses.
-* Developing offline without real service access.
+* When testing systems with external or third-party service dependencies.
+* In integration tests to isolate the service being tested from network or external dependencies.
+* During development when the actual services are unavailable or unreliable.
+* To speed up tests by avoiding calls to slower external systems.
## Real-World Applications of Service Stub Pattern in Java
-* Simulating APIs (payments, recommendation systems) during testing.
-* Bypassing external AI/ML models in tests.
-* Simplifying integration testing.
+* WireMock: Widely used in Java testing to stub HTTP-based external services.
+* Mockito: Allows creating lightweight stubs for dependencies in unit testing.
+* Spring Cloud Contract: Provides contracts and stub servers for services in microservices architectures.
## Benefits and Trade-offs of Service Stub Pattern
Benefits:
-* Reduces dependencies.
-* Provides predictable behavior.
-* Speeds up testing.
+* Simplifies testing by eliminating dependencies on external systems.
+* Speeds up testing processes by removing latency from external network calls.
+* Allows consistent, repeatable, and predictable testing scenarios.
+* Enables parallel test execution, improving overall development productivity.
Trade-offs:
-* Requires maintaining stub logic.
-* May not fully represent real service behavior.
+* Stubs need to be regularly updated to reflect changes in the actual external services.
+* May introduce false confidence if stubs do not accurately represent external system behavior.
+* Can lead to additional overhead and maintenance of stub configurations.
## Related Java Design Patterns
-* [Proxy](https://java-design-patterns.com/patterns/proxy/)
-* [Strategy](https://java-design-patterns.com/patterns/strategy/)
+* [Adapter](https://java-design-patterns.com/patterns/adapter/): Service Stub may sometimes implement Adapter interfaces to mimic external dependencies in a test environment.
+* Mock Object: Similar to Service Stub, but Mock Objects usually verify interactions explicitly, while Service Stubs primarily provide predefined responses without verification.
+* [Proxy](https://java-design-patterns.com/patterns/proxy/): Both Service Stub and Proxy introduce intermediate objects to control access or communication with actual components, though Proxy typically manages access control and communication, while Service Stub specifically aims to isolate for testing.
## References and Credits
-* [Martin Fowler: Test Stubs](https://martinfowler.com/articles/mocksArentStubs.html)
+* [Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation](https://amzn.to/4bjhTSK)
+* [Growing Object-Oriented Software, Guided by Tests](https://amzn.to/4dGfIuk)
+* [Mocks Aren't Stubs (Martin Fowler)](https://martinfowler.com/articles/mocksArentStubs.html)
+* [xUnit Test Patterns: Refactoring Test Code](https://amzn.to/4dHGDpm)
diff --git a/service-stub/src/main/java/com/iluwatar/servicestub/RealSentimentAnalysisServer.java b/service-stub/src/main/java/com/iluwatar/servicestub/RealSentimentAnalysisServer.java
index dc7c174cbe54..c65f6d172151 100644
--- a/service-stub/src/main/java/com/iluwatar/servicestub/RealSentimentAnalysisServer.java
+++ b/service-stub/src/main/java/com/iluwatar/servicestub/RealSentimentAnalysisServer.java
@@ -36,10 +36,8 @@ public class RealSentimentAnalysisServer implements SentimentAnalysisServer {
* A real sentiment analysis implementation would analyze the input string using, e.g., NLP and
* determine whether the sentiment is positive, negative or neutral. Here we simply choose a
* random number to simulate this. The "model" may take some time to process the input and we
- * simulate this by delaying the execution 5 seconds.
- *
- * @param text the input string to analyze
- * @return sentiment classification result (Positive, Negative, or Neutral)
+ * simulate this by delaying the execution 5 seconds. Analyzes the sentiment of the given input
+ * string and returns the classification result (Positive, Negative, or Neutral).
*/
private final Supplier sentimentSupplier;
@@ -61,6 +59,11 @@ public String analyzeSentiment(String text) {
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
- return sentiment == 0 ? "Positive" : sentiment == 1 ? "Negative" : "Neutral";
+
+ return switch (sentiment) {
+ case 0 -> "Positive";
+ case 1 -> "Negative";
+ default -> "Neutral";
+ };
}
}
diff --git a/service-stub/src/test/java/com/iluwatar/servicestub/AppTest.java b/service-stub/src/test/java/com/iluwatar/servicestub/AppTest.java
index 13d2d190dad2..a0acda718b00 100644
--- a/service-stub/src/test/java/com/iluwatar/servicestub/AppTest.java
+++ b/service-stub/src/test/java/com/iluwatar/servicestub/AppTest.java
@@ -28,7 +28,7 @@
import org.junit.jupiter.api.Test;
-public class AppTest {
+class AppTest {
@Test
void shouldExecuteWithoutException() {
assertDoesNotThrow(() -> App.main(new String[] {}));
diff --git a/session-facade/README.md b/session-facade/README.md
index 4724afcfbda3..fb8fe74d7f50 100644
--- a/session-facade/README.md
+++ b/session-facade/README.md
@@ -5,28 +5,34 @@ description: "Learn how to implement the Session Facade Design Pattern in Java t
category: Structural
language: en
tag:
- - Abstraction
+ - API design
+ - Abstraction
+ - Architecture
+ - Business
+ - Decoupling
+ - Enterprise patterns
+ - Facade
+ - Layered architecture
+ - Session management
---
## Also known as
-* Session Facade
+* Remote Facade
## Intent of Session Facade Design Pattern
-Abstracting the underlying business object interactions by providing a service layer that exposes only the required interfaces
+Provide a simplified interface to a complex subsystem, reducing complexity and coupling between client and business logic in enterprise Java applications.
## Detailed Explanation of Session Facade Pattern with Real-World Examples
Real-world example
-> In an e-commerce website, users interact with several subsystems like product catalogs, shopping carts,
-> payment services, and order management. The Session Facade pattern provides a simplified, centralized interface for these subsystems,
-> allowing the client to interact with just a few high-level methods (e.g., addToCart(), placeOrder(), selectPaymentMethod()), instead of directly communicating with each subsystem, using a facade supports low coupling between classes and high cohesion within each service, allowing them to focus on their specific responsibilities.
+> A real-world analogy for the Session Facade pattern is a hotel concierge service. Guests (clients) don't directly interact with various departments like housekeeping, kitchen, transport services, or maintenance. Instead, they interact with the concierge (the facade), who simplifies these interactions. When a guest requests services like room cleaning, dinner reservations, or taxi bookings, the concierge handles communication with multiple hotel departments behind the scenes, providing a simplified and unified interface to the guest, reducing complexity and enhancing guest satisfaction.
In plain words
-> The Session Facade design pattern is an excellent choice for decoupling complex components of the system that need to be interacting frequently.
+> Session Facade provides a simplified interface to complex business logic in Java applications, reducing client complexity and minimizing network overhead by encapsulating interactions within a single session component.
Sequence diagram
@@ -34,14 +40,16 @@ Sequence diagram
## Programmatic Example of Session Facade Pattern in Java
-The Session Facade design pattern is a structural design pattern that provides a simplified interface to a set of complex subsystems, reducing the complexity for the client. This pattern is particularly useful in situations where the client needs to interact with multiple services or systems but doesn’t need to know the internal workings of each service.
+The Session Facade pattern is a structural design pattern that provides a simplified interface to complex subsystems, making the system easier for clients to interact with. It is especially useful when a client needs access to multiple underlying services without needing to understand their internal complexities.
-In the context of an e-commerce website, imagine a system where users can browse products, add items to the shopping cart, process payments, and place orders. Instead of the client directly interacting with each individual service (cart, order, payment), the Session Facade provides a single, unified interface for these operations.
+In the context of an e-commerce website, consider a scenario where users browse products, manage their shopping carts, place orders, and process payments. Rather than directly interacting with each subsystem individually (such as the cart, order, and payment systems), a client can communicate through a single unified Session Facade interface.
-Example Scenario:
-In this example, the ShoppingFacade class manages interactions with three subsystems: the `CartService`, `OrderService`, and `PaymentService`. The client interacts with the facade to perform high-level operations like adding items to the cart, placing an order, and selecting a payment method.
+### Example Scenario:
+
+In this example, the `ShoppingFacade` class simplifies client interactions with three services: the `CartService`, `OrderService`, and `PaymentService`. The client uses the facade to perform high-level operations like adding products to the cart, placing an order, and choosing a payment method, without needing to know the underlying details.
+
+Here's a simplified Java program demonstrating this pattern:
-Here’s a simplified programmatic example:
```java
public class App {
public static void main(String[] args) {
@@ -53,14 +61,15 @@ public class App {
}
```
-The `ShoppingFacade` acts as an intermediary that facilitates interaction between different services promoting low coupling between these services.
+The `ShoppingFacade` serves as a centralized point of interaction for various shopping-related operations, thereby reducing direct coupling between client code and individual subsystem services:
+
```java
public class ShoppingFacade {
-
+
private final CartService cartService;
private final OrderService orderService;
private final PaymentService paymentService;
-
+
public ShoppingFacade() {
Map productCatalog = new HashMap<>();
productCatalog.put(1, new Product(1, "Wireless Mouse", 25.99, "Ergonomic wireless mouse with USB receiver."));
@@ -70,42 +79,45 @@ public class ShoppingFacade {
orderService = new OrderService(cart);
paymentService = new PaymentService();
}
-
+
public Map getCart() {
return this.cartService.getCart();
}
-
+
public void addToCart(int productId) {
this.cartService.addToCart(productId);
}
-
+
public void removeFromCart(int productId) {
this.cartService.removeFromCart(productId);
}
-
+
public void order() {
this.orderService.order();
}
-
+
public Boolean isPaymentRequired() {
double total = this.orderService.getTotal();
- if (total == 0.0) {
+ if (total==0.0) {
LOGGER.info("No payment required");
return false;
}
return true;
}
-
+
public void processPayment(String method) {
Boolean isPaymentRequired = isPaymentRequired();
if (Boolean.TRUE.equals(isPaymentRequired)) {
paymentService.selectPaymentMethod(method);
}
}
+}
```
-Console output for starting the `App` class's `main` method:
+### Console Output
+
+When running the provided example (App.main()), the output might look similar to:
```
19:43:17.883 [main] INFO com.iluwatar.sessionfacade.CartService -- ID: 1
@@ -115,41 +127,40 @@ Description: Ergonomic wireless mouse with USB receiver. successfully added to t
19:43:17.910 [main] INFO com.iluwatar.sessionfacade.OrderService -- Client has chosen to order [ID: 1
```
-This is a basic example of the Session Facade design pattern. The actual implementation would depend on specific requirements of your application.
+This simplified example demonstrates the essence of the Session Facade pattern. Your actual implementation may vary based on the specific needs of your application.
## When to Use the Session Facade Pattern in Java
-* Use when building complex applications with multiple interacting services, where you want to simplify the interaction between various subsystems.
-* Ideal for decoupling complex systems that need to interact but should not be tightly coupled.
-* Suitable for applications where you need a single point of entry to interact with multiple backend services, like ecommerce platforms, booking systems, or order management systems.
+* When dealing with complex enterprise applications containing multiple business objects.
+* To provide simplified API calls to clients, hiding the underlying complexity.
+* When seeking improved performance and reduced network calls between clients and servers.
## Real-World Applications of Server Session Pattern in Java
-* Enterprise JavaBeans (EJB)
-* Java EE (Jakarta EE) Applications
+* Java EE applications utilizing Enterprise JavaBeans (EJB) as session facades to encapsulate business logic.
+* Spring-based applications using services as session facades to simplify interactions between controllers and repositories.
## Benefits and Trade-offs of Server Session Pattern
+Benefits:
-* Simplifies client-side logic by providing a single entry point for complex operations across multiple services.
-* Decouples components of the application, making them easier to maintain, test, and modify without affecting other parts of the system.
-* Improves modularity by isolating the implementation details of subsystems from the client.
-* Centralizes business logic in one place, making the code easier to manage and update.
+* Reduces complexity by providing a simpler interface to a subsystem.
+* Improves performance by minimizing network traffic and reducing remote calls.
+* Enhances modularity and maintainability by clearly separating business logic and client interactions.
-## Trade-offs:
+Trade-offs:
-* Potential performance bottleneck: Since all requests pass through the facade, it can become a bottleneck if not optimized.
-* Increased complexity: If the facade becomes too large or complex, it could counteract the modularity it aims to achieve.
-* Single point of failure: If the facade encounters issues, it could affect the entire system's operation, making it crucial to handle errors and exceptions properly.
+* Can introduce additional layers that might increase initial complexity.
+* Risk of creating overly broad facades that violate single responsibility principles.
## Related Java Design Patterns
-* [Facade](https://java-design-patterns.com/patterns/facade/): The Session Facade pattern is a specific application of the more general Facade pattern, which simplifies access to complex subsystems.
-* [Command](https://java-design-patterns.com/patterns/command/): Useful for encapsulating requests and passing them to the session facade, which could then manage the execution order.
-* [Singleton](https://java-design-patterns.com/patterns/singleton/): Often used to create a single instance of the session facade for managing the entire workflow of a subsystem.
+* [Data Transfer Object (DTO)](https://java-design-patterns.com/patterns/data-transfer-object/): Often used together, Session Facade simplifies data transfer by utilizing DTOs to encapsulate data passed between client and server.
+* [Facade](https://java-design-patterns.com/patterns/facade/): Session Facade is a specialized version of the Facade pattern, applied specifically in enterprise systems to manage business logic and remote interactions.
## References and Credits
* [Core J2EE Patterns: Best Practices and Design Strategies](https://amzn.to/4cAbDap)
-* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
* [Patterns of Enterprise Application Architecture](https://amzn.to/3WfKBPR)
+* [Real World Java EE Patterns-Rethinking Best Practices](https://amzn.to/3EvkzS8)
+* [Remote Facade (Martin Fowler)](https://martinfowler.com/eaaCatalog/remoteFacade.html)
diff --git a/session-facade/pom.xml b/session-facade/pom.xml
index 6dada70f5f75..befdb72d7423 100644
--- a/session-facade/pom.xml
+++ b/session-facade/pom.xml
@@ -50,6 +50,11 @@
junit-jupiter-engine
test
+
+ org.junit.jupiter
+ junit-jupiter-params
+ test
+
org.mockito
mockito-core
diff --git a/session-facade/src/test/java/com/iluwatar/sessionfacade/AppTest.java b/session-facade/src/test/java/com/iluwatar/sessionfacade/AppTest.java
index 707fd944efb8..556c9536d0e9 100644
--- a/session-facade/src/test/java/com/iluwatar/sessionfacade/AppTest.java
+++ b/session-facade/src/test/java/com/iluwatar/sessionfacade/AppTest.java
@@ -27,7 +27,7 @@
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
/** The type App test. */
-public class AppTest {
+class AppTest {
/** Should execute application without exception. */
@org.junit.jupiter.api.Test
diff --git a/session-facade/src/test/java/com/iluwatar/sessionfacade/PaymentServiceTest.java b/session-facade/src/test/java/com/iluwatar/sessionfacade/PaymentServiceTest.java
index 609cc3559bd2..06852f6d3b31 100644
--- a/session-facade/src/test/java/com/iluwatar/sessionfacade/PaymentServiceTest.java
+++ b/session-facade/src/test/java/com/iluwatar/sessionfacade/PaymentServiceTest.java
@@ -27,13 +27,13 @@
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
import org.slf4j.Logger;
/** The type Payment service test. */
class PaymentServiceTest {
private PaymentService paymentService;
- private OrderService orderService;
private Logger mockLogger;
/** Sets up. */
@@ -44,27 +44,14 @@ void setUp() {
paymentService.LOGGER = mockLogger;
}
- /** Test select cash payment method. */
- @Test
- void testSelectCashPaymentMethod() {
- String method = "cash";
+ @ParameterizedTest
+ @CsvSource({
+ "cash, Client have chosen cash payment option",
+ "credit, Client have chosen credit card payment option",
+ "cheque, Unspecified payment method type"
+ })
+ void testSelectPaymentMethod(String method, String expectedLogMessage) {
paymentService.selectPaymentMethod(method);
- verify(mockLogger).info("Client have chosen cash payment option");
- }
-
- /** Test select credit card payment method. */
- @Test
- void testSelectCreditCardPaymentMethod() {
- String method = "credit";
- paymentService.selectPaymentMethod(method);
- verify(mockLogger).info("Client have chosen credit card payment option");
- }
-
- /** Test select unspecified payment method. */
- @Test
- void testSelectUnspecifiedPaymentMethod() {
- String method = "cheque";
- paymentService.selectPaymentMethod(method);
- verify(mockLogger).info("Unspecified payment method type");
+ verify(mockLogger).info(expectedLogMessage);
}
}
diff --git a/table-inheritance/README.md b/table-inheritance/README.md
index 53e9b2ce002d..fc9f7500286c 100644
--- a/table-inheritance/README.md
+++ b/table-inheritance/README.md
@@ -2,191 +2,255 @@
title: "Table Inheritance Pattern in Java: Modeling Hierarchical Data in Relational Databases"
shortTitle: Table Inheritance
description: "Explore the Table Inheritance pattern in Java with real-world examples, database schema, and tutorials. Learn how to model class hierarchies elegantly in relational databases."
-category: Structural
+category: Data access
language: en
tag:
- - Decoupling
+ - Data access
+ - Database
+ - Inheritance
+ - Persistence
+ - Polymorphism
---
+## Also known as
+
+* Class Table Inheritance
+* Joined Table Inheritance
+
## Intent of Table Inheritance Pattern
-The Table Inheritance pattern models a class hierarchy in a relational database by creating
-separate tables for each class in the hierarchy. These tables share a common primary key, which in
-subclass tables also serves as a foreign key referencing the primary key of the base class table.
-This linkage maintains relationships and effectively represents the inheritance structure. This pattern
-enables the organization of complex data models, particularly when subclasses have unique properties
-that must be stored in distinct tables.
----
+Represent inheritance hierarchies in relational databases by mapping each class in a hierarchy to a database table.
## Detailed Explanation of Table Inheritance Pattern with Real-World Examples
-### Real-World Example
-Consider a **Vehicle Management System** with a `Vehicle` superclass and subclasses like `Car` and `Truck`.
+Real-world example
-- The **Vehicle Table** stores attributes common to all vehicles, such as `make`, `model`, and `year`. Its primary key (`id`) uniquely identifies each vehicle.
-- The **Car Table** and **Truck Table** store attributes specific to their respective types, such as `numberOfDoors` for cars and `payloadCapacity` for trucks.
-- The `id` column in the **Car Table** and **Truck Table** serves as both the primary key for those tables and a foreign key referencing the `id` in the **Vehicle Table**.
+> A classic real-world analogy for the Table Inheritance (Joined Table) pattern is managing employee records in an organization:
+> Imagine a company's database storing information about employees. All employees have common attributes (name, employee ID, hire date), stored in a general "Employee" table. However, the company also has different types of employees: Full-time Employees (with a salary and benefits) and Contractors (hourly rate, contract duration). Each employee type has distinct data stored in separate specialized tables ("FullTimeEmployee" and "Contractor"), which reference the main "Employee" table.
+> This structure mirrors the Table Inheritance pattern—shared fields in a common table and unique fields split into subclass-specific tables.
-This setup ensures each subclass entry corresponds to a base class entry, maintaining the inheritance relationship while keeping subclass-specific data in their own tables.
+In plain words
-### In Plain Words
-In table inheritance, each class in the hierarchy is represented by a separate table, which
-allows for a clear distinction between shared attributes (stored in the base class table) and
-specific attributes (stored in subclass tables).
+> The Table Inheritance pattern maps each class within an inheritance hierarchy to its own database table, storing common attributes in a base table and subclass-specific attributes in separate joined tables.
-### Martin Fowler Says
+Martin Fowler says
-Relational databases don't support inheritance, which creates a mismatch when mapping objects.
-To fix this, Table Inheritance uses a separate table for each class in the hierarchy while maintaining
-relationships through foreign keys, making it easier to link the classes together in the database.
+> Relational databases don't support inheritance, which creates a mismatch when mapping objects. To fix this, Table Inheritance uses a separate table for each class in the hierarchy while maintaining relationships through foreign keys, making it easier to link the classes together in the database.
-For more detailed information, refer to Martin Fowler's article on [Class Table Inheritance](https://martinfowler.com/eaaCatalog/classTableInheritance.html).
+Mind map
+
## Programmatic Example of Table Inheritance Pattern in Java
-
-The `Vehicle` class will be the superclass, and we will have `Car` and `Truck` as subclasses that extend
-`Vehicle`. The `Vehicle` class will store common attributes, while `Car` and `Truck` will store
-attributes specific to those subclasses.
+The `Vehicle` class will be the superclass, and we will have subclasses `Car` and `Truck` that extend `Vehicle`. The superclass `Vehicle` stores common attributes, while subclasses store their own specific attributes.
### Key Aspects of the Pattern:
-1. **Superclass (`Vehicle`)**:
- The `Vehicle` class stores attributes shared by all vehicle types, such as:
- - `make`: The manufacturer of the vehicle.
- - `model`: The model of the vehicle.
- - `year`: The year the vehicle was manufactured.
- - `id`: A unique identifier for the vehicle.
+**Superclass (`Vehicle`):**
+
+The superclass stores shared attributes:
+
+* `make`: Manufacturer of the vehicle.
+* `model`: Model of the vehicle.
+* `year`: Year of manufacture.
+* `id`: Unique identifier for the vehicle.
- These attributes are stored in the **`Vehicle` table** in the database.
+These common attributes will reside in a dedicated database table (`Vehicle` table).
-2. **Subclass (`Car` and `Truck`)**:
- Each subclass (`Car` and `Truck`) stores attributes specific to that vehicle type:
- - `Car`: Has an additional attribute `numberOfDoors` representing the number of doors the car has.
- - `Truck`: Has an additional attribute `payloadCapacity` representing the payload capacity of the truck.
+**Subclasses (`Car` and `Truck`):**
- These subclass-specific attributes are stored in the **`Car` and `Truck` tables**.
+Each subclass adds attributes specific to its type:
-3. **Foreign Key Relationship**:
- Each subclass (`Car` and `Truck`) contains the `id` field which acts as a **foreign key** that
-references the primary key (`id`) of the superclass (`Vehicle`). This foreign key ensures the
-relationship between the common attributes in the `Vehicle` table and the specific attributes in the
-subclass tables (`Car` and `Truck`).
+* `Car`: `numberOfDoors`, indicating how many doors the car has.
+* `Truck`: `payloadCapacity`, representing how much payload the truck can carry.
+Each subclass stores these specific attributes in their respective tables (`Car` and `Truck` tables).
+
+**Foreign Key Relationship:**
+
+Each subclass table references the superclass table via a foreign key. The subclass's `id` links to the primary key of the superclass, thus connecting common and subclass-specific data.
+
+### Java Implementation Using JPA Annotations:
```java
-/**
- * Superclass
- * Represents a generic vehicle with basic attributes like make, model, year, and ID.
- */
+@Setter
+@Getter
public class Vehicle {
- private String make;
- private String model;
- private int year;
- private int id;
- // Constructor, getters, and setters...
+ private String make;
+ private String model;
+ private int year;
+ private int id;
+
+ public Vehicle(int year, String make, String model, int id) {
+ this.make = make;
+ this.model = model;
+ this.year = year;
+ this.id = id;
+ }
+
+ @Override
+ public String toString() {
+ return "Vehicle{"
+ + "id="
+ + id
+ + ", make='"
+ + make
+ + '\''
+ + ", model='"
+ + model
+ + '\''
+ + ", year="
+ + year
+ + '}';
+ }
}
-/**
- * Represents a car, which is a subclass of Vehicle.
- */
+@Getter
public class Car extends Vehicle {
- private int numberOfDoors;
-
- // Constructor, getters, and setters...
+ private int numDoors;
+
+ public Car(int year, String make, String model, int numDoors, int id) {
+ super(year, make, model, id);
+ if (numDoors <= 0) {
+ throw new IllegalArgumentException("Number of doors must be positive.");
+ }
+ this.numDoors = numDoors;
+ }
+
+ public void setNumDoors(int doors) {
+ if (doors <= 0) {
+ throw new IllegalArgumentException("Number of doors must be positive.");
+ }
+ this.numDoors = doors;
+ }
+
+ @Override
+ public String toString() {
+ return "Car{"
+ + "id="
+ + getId()
+ + ", make='"
+ + getMake()
+ + '\''
+ + ", model='"
+ + getModel()
+ + '\''
+ + ", year="
+ + getYear()
+ + ", numberOfDoors="
+ + getNumDoors()
+ + '}';
+ }
}
-/**
- * Represents a truck, which is a subclass of Vehicle.
- */
+@Getter
public class Truck extends Vehicle {
- private int payloadCapacity;
-
- // Constructor, getters, and setters...
+ private double loadCapacity;
+
+ public Truck(int year, String make, String model, double loadCapacity, int id) {
+ super(year, make, model, id);
+ if (loadCapacity <= 0) {
+ throw new IllegalArgumentException("Load capacity must be positive.");
+ }
+ this.loadCapacity = loadCapacity;
+ }
+
+ public void setLoadCapacity(double capacity) {
+ if (capacity <= 0) {
+ throw new IllegalArgumentException("Load capacity must be positive.");
+ }
+ this.loadCapacity = capacity;
+ }
+
+ @Override
+ public String toString() {
+ return "Truck{"
+ + "id="
+ + getId()
+ + ", make='"
+ + getMake()
+ + '\''
+ + ", model='"
+ + getModel()
+ + '\''
+ + ", year="
+ + getYear()
+ + ", payloadCapacity="
+ + getLoadCapacity()
+ + '}';
+ }
}
```
+### Explanation of the JPA annotations used above:
+* `@Entity`: Indicates that the class is a JPA entity mapped to a database table.
+* `@Inheritance(strategy = InheritanceType.JOINED)`: Configures joined table inheritance, meaning each class (superclass and subclasses) maps to its own table.
+* `@Table(name = "XYZ")`: Explicitly specifies the database table name for clarity.
+* `@Id`: Marks the primary key of the entity.
+* `@GeneratedValue(strategy = GenerationType.IDENTITY)`: Specifies auto-generation of primary key values by the database.
-## Table Inheritance Pattern Class Diagram
+### Database Structure Result:
+Applying this code will result in three database tables structured as follows:
-
+**Vehicle table**
+* id
+* make
+* model
+* year
+**Car table**
+* id (FK to Vehicle)
+* numberOfDoors
+**Truck table**
+* id (FK to Vehicle)
+* payloadCapacity
-
-
-
-## Table Inheritance Pattern Database Schema
-
-### Vehicle Table
-| Column | Description |
-|--------|-------------------------------------|
-| id | Primary key |
-| make | The make of the vehicle |
-| model | The model of the vehicle |
-| year | The manufacturing year of the vehicle |
-
-### Car Table
-| Column | Description |
-|------------------|-------------------------------------|
-| id | Foreign key referencing `Vehicle(id)` |
-| numberOfDoors | Number of doors in the car |
-
-### Truck Table
-| Column | Description |
-|-------------------|-------------------------------------|
-| id | Foreign key referencing `Vehicle(id)` |
-| payloadCapacity | Payload capacity of the truck |
-
----
+This approach clearly represents the Table Inheritance (Joined Table) pattern, with common attributes centrally managed in the superclass table and subclass-specific attributes cleanly separated in their own tables.
## When to Use the Table Inheritance Pattern in Java
-- When your application requires a clear mapping of an object-oriented class hierarchy to relational tables.
-- When subclasses have unique attributes that do not fit into a single base table.
-- When scalability and normalization of data are important considerations.
-- When you need to separate concerns and organize data in a way that each subclass has its own
-table but maintains relationships with the superclass.
+* When persisting an inheritance hierarchy of Java classes in a relational database.
+* Suitable when classes share common attributes but also have distinct fields.
+* Beneficial when polymorphic queries across subclasses are frequent.
## Table Inheritance Pattern Java Tutorials
- [Software Patterns Lexicon: Class Table Inheritance](https://softwarepatternslexicon.com/patterns-sql/4/4/2/)
- [Martin Fowler: Class Table Inheritance](http://thierryroussel.free.fr/java/books/martinfowler/www.martinfowler.com/isa/classTableInheritance.html)
----
-
## Real-World Applications of Table Inheritance Pattern in Java
-- **Vehicle Management System**: Used to store different types of vehicles like Car and Truck in separate tables but maintain a relationship through a common superclass `Vehicle`.
-- **E-Commerce Platforms**: Where different product types, such as Clothing, Electronics, and Furniture, are stored in separate tables with shared attributes in a superclass `Product`.
+* Hibernate ORM (`@Inheritance(strategy = InheritanceType.JOINED)` in Java)
+* EclipseLink (Joined Inheritance strategy in JPA)
+* Spring Data JPA applications modeling complex domain hierarchies.
## Benefits and Trade-offs of Table Inheritance Pattern
-### Benefits
+Benefits:
-- **Clear Structure**: Each class has its own table, making the data model easier to maintain and understand.
-- **Scalability**: Each subclass can be extended independently without affecting the other tables, making the system more scalable.
-- **Data Normalization**: Helps avoid data redundancy and keeps the schema normalized.
+ * Normalized database schema reduces redundancy.
+ * Clearly models class hierarchies at the database level.
+ * Easier to implement polymorphic queries due to clear class distinctions.
-### Trade-offs
+Trade-offs:
-- **Multiple Joins**: Retrieving data that spans multiple subclasses may require joining multiple tables, which could lead to performance issues.
-- **Increased Complexity**: Managing relationships between tables and maintaining integrity can become more complex.
-- **Potential for Sparse Tables**: Subclasses with fewer attributes may end up with tables that have many null fields.
+ * Increased complexity in database queries involving multiple joins.
+ * Reduced performance for deep inheritance hierarchies due to costly joins.
+ * Maintenance overhead increases with the complexity of inheritance structures.
## Related Java Design Patterns
-- **Single Table Inheritance** – A strategy where a single table is used to store all classes in an
-inheritance hierarchy. It stores all attributes of the class and its subclasses in one table.
-- **Singleton Pattern** – Used when a class needs to have only one instance.
-
+* [Single Table Inheritance](https://java-design-patterns.com/patterns/single-table-inheritance/): Alternative strategy mapping an entire class hierarchy into a single database table, useful when fewer joins are preferred at the cost of nullable columns.
+* Concrete Table Inheritance – Each subclass has its own standalone table; related in providing an alternate approach to storing inheritance hierarchies.
## References and Credits
-- **Martin Fowler** - [*Patterns of Enterprise Application Architecture*](https://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420)
-- **Java Persistence with Hibernate** - [Link to book](https://www.amazon.com/Java-Persistence-Hibernate-Christian-Bauer/dp/193239469X)
-- **Object-Relational Mapping on Wikipedia** - [Link to article](https://en.wikipedia.org/wiki/Object-relational_mapping)
+* [Java Persistence with Hibernate](https://amzn.to/44tP1ox)
+* [Object-Relational Mapping (Wikipedia)](https://en.wikipedia.org/wiki/Object-relational_mapping)
+* [Patterns of Enterprise Application Architecture](https://amzn.to/3WfKBPR)
+* [Pro JPA 2: Mastering the Java Persistence API](https://amzn.to/4b7UoMC)
diff --git a/table-inheritance/etc/table-inheritance-mind-map.png b/table-inheritance/etc/table-inheritance-mind-map.png
new file mode 100644
index 000000000000..c403077b667b
Binary files /dev/null and b/table-inheritance/etc/table-inheritance-mind-map.png differ
diff --git a/table-inheritance/src/test/java/AppTest.java b/table-inheritance/src/test/java/com/iluwatar/table/inheritance/AppTest.java
similarity index 53%
rename from table-inheritance/src/test/java/AppTest.java
rename to table-inheritance/src/test/java/com/iluwatar/table/inheritance/AppTest.java
index 1d8e271423f7..8cbae5b2bfb9 100644
--- a/table-inheritance/src/test/java/AppTest.java
+++ b/table-inheritance/src/test/java/com/iluwatar/table/inheritance/AppTest.java
@@ -22,9 +22,33 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+package com.iluwatar.table.inheritance; /*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
import static org.junit.jupiter.api.Assertions.assertTrue;
-import com.iluwatar.table.inheritance.App;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.logging.ConsoleHandler;
diff --git a/table-inheritance/src/test/java/VehicleDatabaseTest.java b/table-inheritance/src/test/java/com/iluwatar/table/inheritance/VehicleDatabaseTest.java
similarity index 75%
rename from table-inheritance/src/test/java/VehicleDatabaseTest.java
rename to table-inheritance/src/test/java/com/iluwatar/table/inheritance/VehicleDatabaseTest.java
index 71461f990a76..9c290fd5c325 100644
--- a/table-inheritance/src/test/java/VehicleDatabaseTest.java
+++ b/table-inheritance/src/test/java/com/iluwatar/table/inheritance/VehicleDatabaseTest.java
@@ -22,14 +22,35 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+package com.iluwatar.table.inheritance; /*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 Ilkka Seppälä
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import com.iluwatar.table.inheritance.Car;
-import com.iluwatar.table.inheritance.Truck;
-import com.iluwatar.table.inheritance.Vehicle;
-import com.iluwatar.table.inheritance.VehicleDatabase;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -43,7 +64,7 @@ class VehicleDatabaseTest {
/** Sets up a new instance of {@link VehicleDatabase} before each test. */
@BeforeEach
- public void setUp() {
+ void setUp() {
vehicleDatabase = new VehicleDatabase();
}