diff --git a/.all-contributorsrc b/.all-contributorsrc index b2fef6640890..afa0038a4141 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -3493,6 +3493,24 @@ "contributions": [ "code" ] + }, + { + "login": "ssrijan-007-sys", + "name": "ssrijan-007-sys", + "avatar_url": "https://avatars.githubusercontent.com/u/137605821?v=4", + "profile": "https://github.com/ssrijan-007-sys", + "contributions": [ + "code" + ] + }, + { + "login": "e5LA", + "name": "e5LA", + "avatar_url": "https://avatars.githubusercontent.com/u/208197507?v=4", + "profile": "https://github.com/e5LA", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 6, diff --git a/README.md b/README.md index 881563902e26..0b9b5b0d90ad 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=iluwatar_java-design-patterns&metric=coverage)](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns) [![Join the chat at https://gitter.im/iluwatar/java-design-patterns](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![All Contributors](https://img.shields.io/badge/all_contributors-383-orange.svg?style=flat-square)](#contributors-) +[![All Contributors](https://img.shields.io/badge/all_contributors-385-orange.svg?style=flat-square)](#contributors-)
@@ -45,7 +45,7 @@ If you are willing to contribute to the project you will find the relevant infor # The Book -The design patterns are now available as an e-book. Find out more about "Open Source Java Design Patterns" here: https://payhip.com/b/kcaF9 +The design patterns are now available as an e-book. Find out more about "Open Source Java Design Patterns" here: https://payhip.com/b/bNQFX The project contributors can get the book for free. Contact the maintainer via [Gitter chatroom](https://gitter.im/iluwatar/java-design-patterns) or email (iluwatar (at) gmail (dot) com ). Send a message that contains your email address, Github username, and a link to an accepted pull request. @@ -570,6 +570,10 @@ This project is licensed under the terms of the MIT license. Sanura Hettiarachchi
Sanura Hettiarachchi

💻 Kim Gi Uk
Kim Gi Uk

💻 Suchismita Deb
Suchismita Deb

💻 + ssrijan-007-sys
ssrijan-007-sys

💻 + + + e5LA
e5LA

💻 diff --git a/actor-model/README.md b/actor-model/README.md new file mode 100644 index 000000000000..be8065ffefef --- /dev/null +++ b/actor-model/README.md @@ -0,0 +1,201 @@ +--- +title: "Actor Model Pattern in Java: Building Concurrent Systems with Elegance" +shortTitle: Actor Model +description: "Explore the Actor Model pattern in Java with real-world examples and practical implementation. Learn how to build scalable, message-driven systems using actors, messages, and asynchronous communication." +category: Concurrency +language: en +tag: + - Concurrency + - Messaging + - Isolation + - Asynchronous + - Distributed Systems + - Actor Model +--- + +## Also Known As + +- Message-passing concurrency +- Actor-based concurrency + +--- + +## Intent of Actor Model Pattern + +The Actor Model pattern enables the construction of highly concurrent, distributed, and fault-tolerant systems by using isolated components (actors) that interact exclusively through asynchronous message passing. + +--- + +## Detailed Explanation of Actor Model Pattern with Real-World Examples + +### 📦 Real-world Example + +Imagine a customer service system: +- Each **customer support agent** is an **actor**. +- Customers **send questions (messages)** to agents. +- Each agent handles one request at a time and can **respond asynchronously** without interfering with other agents. + +--- + +### 🧠 In Plain Words + +> "Actors are like independent workers that never share memory and only communicate through messages." + +--- + +### 📖 Wikipedia Says + +> [Actor model](https://en.wikipedia.org/wiki/Actor_model) is a mathematical model of concurrent computation that treats "actors" as the universal primitives of concurrent computation. + +--- + +### 🧹 Architecture Diagram + +![UML Class Diagram](./etc/Actor_Model_UML_Class_Diagram.png) + +--- + +## Programmatic Example of Actor Model Pattern in Java + +### Actor.java + +```java +public abstract class Actor implements Runnable { + + @Setter @Getter private String actorId; + private final BlockingQueue mailbox = new LinkedBlockingQueue<>(); + private volatile boolean active = true; + + + public void send(Message message) { + mailbox.add(message); + } + + public void stop() { + active = false; + } + + @Override + public void run() { + + } + + protected abstract void onReceive(Message message); +} + +``` + +### Message.java + +```java + +@AllArgsConstructor +@Getter +@Setter +public class Message { + private final String content; + private final String senderId; +} +``` + +### ActorSystem.java + +```java +public class ActorSystem { + 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 + } +} +``` + +### App.java + +```java +public class App { + public static void main(String[] args) { + 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 + } +} +``` + +--- + +## When to Use the Actor Model Pattern in Java + +- When building **concurrent or distributed systems** +- When you want **no shared mutable state** +- When you need **asynchronous, message-driven communication** +- When components should be **isolated and loosely coupled** + +--- + +## Actor Model Pattern Java Tutorials + +- [Baeldung – Akka with Java](https://www.baeldung.com/java-akka) +- [Vaughn Vernon – Reactive Messaging Patterns](https://vaughnvernon.co/?p=1143) + +--- + +## Real-World Applications of Actor Model Pattern in Java + +- [Akka Framework](https://akka.io/) +- [Erlang and Elixir concurrency](https://www.erlang.org/) +- [Microsoft Orleans](https://learn.microsoft.com/en-us/dotnet/orleans/) +- JVM-based game engines and simulators + +--- + +## Benefits and Trade-offs of Actor Model Pattern + +### ✅ Benefits +- High concurrency support +- Easy scaling across threads or machines +- Fault isolation and recovery +- Message ordering within actors + +### ⚠️ Trade-offs +- Harder to debug due to asynchronous behavior +- Slight performance overhead due to message queues +- More complex to design than simple method calls + +--- + +## Related Java Design Patterns + +- [Command Pattern](../command) +- [Mediator Pattern](../mediator) +- [Event-Driven Architecture](../event-driven-architecture) +- [Observer Pattern](../observer) + +--- + +## References and Credits + +- *Programming Erlang*, Joe Armstrong +- *Reactive Design Patterns*, Roland Kuhn +- *The Actor Model in 10 Minutes*, [InfoQ Article](https://www.infoq.com/articles/actor-model/) +- [Akka Documentation](https://doc.akka.io/docs/akka/current/index.html) + diff --git a/actor-model/etc/Actor_Model_UML_Class_Diagram.png b/actor-model/etc/Actor_Model_UML_Class_Diagram.png new file mode 100644 index 000000000000..a4c34d7a75fd Binary files /dev/null and b/actor-model/etc/Actor_Model_UML_Class_Diagram.png differ diff --git a/actor-model/etc/actor-model.urm.puml b/actor-model/etc/actor-model.urm.puml new file mode 100644 index 000000000000..020c1fc735a4 --- /dev/null +++ b/actor-model/etc/actor-model.urm.puml @@ -0,0 +1,35 @@ +@startuml actor-model + +title Actor Model - UML Class Diagram + +class ActorSystem { + +actorOf(actor: Actor): Actor + +shutdown(): void +} + +class Actor { + -mailbox: BlockingQueue + -active: boolean + +send(message: Message): void + +stop(): void + +run(): void + #onReceive(message: Message): void +} + +class ExampleActor { + +onReceive(message: Message): void +} + +class Message { + -content: String + -sender: Actor + +getContent(): String + +getSender(): Actor +} + +ActorSystem --> Actor : creates +Actor <|-- ExampleActor : extends +Actor --> Message : processes +ExampleActor --> Message : uses + +@enduml diff --git a/actor-model/pom.xml b/actor-model/pom.xml new file mode 100644 index 000000000000..76c288829b8d --- /dev/null +++ b/actor-model/pom.xml @@ -0,0 +1,114 @@ + + + + + 4.0.0 + + + com.iluwatar + java-design-patterns + 1.26.0-SNAPSHOT + + + actor-model + Actor Model + + + + + + org.junit + junit-bom + 5.11.0 + pom + import + + + + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.platform + junit-platform-launcher + test + + + org.slf4j + slf4j-api + + + ch.qos.logback + logback-classic + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.3.0 + + + jar-with-dependencies + + + + com.iluwatar.actormodel.App + + + + + + make-assembly + package + + single + + + + + + + + + diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/Actor.java b/actor-model/src/main/java/com/iluwatar/actormodel/Actor.java new file mode 100644 index 000000000000..6e2aaccd1937 --- /dev/null +++ b/actor-model/src/main/java/com/iluwatar/actormodel/Actor.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.actormodel; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import lombok.Getter; +import lombok.Setter; + +public abstract class Actor implements Runnable { + + @Setter @Getter private String actorId; + private final BlockingQueue mailbox = new LinkedBlockingQueue<>(); + private volatile boolean active = + true; // always read from main memory and written back to main memory, + + // rather than being cached in a thread's local memory. To make it consistent to all Actors + + public void send(Message message) { + mailbox.add(message); // Add message to queue + } + + public void stop() { + active = false; // Stop the actor loop + } + + @Override + public void run() { + while (active) { + try { + Message message = mailbox.take(); // Wait for a message + onReceive(message); // Process it + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + + // Child classes must define what to do with a message + protected abstract void onReceive(Message message); +} diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/ActorSystem.java b/actor-model/src/main/java/com/iluwatar/actormodel/ActorSystem.java new file mode 100644 index 000000000000..db7c21cb6088 --- /dev/null +++ b/actor-model/src/main/java/com/iluwatar/actormodel/ActorSystem.java @@ -0,0 +1,51 @@ +/* + * 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.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; + +public class ActorSystem { + private final ExecutorService executor = Executors.newCachedThreadPool(); + private final ConcurrentHashMap 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 - -![Data Access Object](./etc/dao.png "Data Access Object") - ## 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 - -![Data Locality](./etc/data-locality.urm.png "Data Locality pattern class diagram") - ## 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 class diagram](./etc/abstract-document.png "Abstract Document class diagram") + +## مثال برنامه‌نویسی از الگوی Abstract Document در جاوا + +فرض کنید یک خودرو داریم که از قطعات مختلفی تشکیل شده است. اما نمی‌دانیم آیا این خودرو خاص واقعاً همه قطعات را دارد یا فقط برخی از آن‌ها. خودروهای ما پویا و بسیار انعطاف‌پذیر هستند. + +بیایید ابتدا کلاس‌های پایه `Document` و `AbstractDocument` را تعریف کنیم. این کلاس‌ها اساساً یک شیء را قادر می‌سازند تا یک نقشه از ویژگی‌ها و هر تعداد شیء فرزند را نگه دارد. + +```java +public interface Document { + + Void put(String key, Object value); + + Object get(String key); + + Stream children(String key, Function, T> constructor); +} + +public abstract class AbstractDocument implements Document { + + private final Map properties; + + protected AbstractDocument(Map properties) { + Objects.requireNonNull(properties, "properties map is required"); + this.properties = properties; + } + + @Override + public Void put(String key, Object value) { + properties.put(key, value); + return null; + } + + @Override + public Object get(String key) { + return properties.get(key); + } + + @Override + public Stream children(String key, Function, T> constructor) { + return Stream.ofNullable(get(key)) + .filter(Objects::nonNull) + .map(el -> (List>) el) + .findAny() + .stream() + .flatMap(Collection::stream) + .map(constructor); + } + + // Other properties and methods... +} +``` +در ادامه، یک enum به نام Property و مجموعه‌ای از واسط‌ها برای type، price، model و parts تعریف می‌کنیم. این کار به ما اجازه می‌دهد یک واسط با ظاهر استاتیک برای کلاس Car ایجاد کنیم. +```java +public enum Property { + + PARTS, TYPE, PRICE, MODEL +} + +public interface HasType extends Document { + + default Optional getType() { + return Optional.ofNullable((String) get(Property.TYPE.toString())); + } +} + +public interface HasPrice extends Document { + + default Optional getPrice() { + return Optional.ofNullable((Number) get(Property.PRICE.toString())); + } +} + +public interface HasModel extends Document { + + default Optional getModel() { + return Optional.ofNullable((String) get(Property.MODEL.toString())); + } +} + +public interface HasParts extends Document { + + default Stream getParts() { + return children(Property.PARTS.toString(), Part::new); + } +} + +public class Part extends AbstractDocument implements HasType, HasModel, HasPrice { + + public Part(Map properties) { + super(properties); + } +} +``` +اکنون آماده‌ایم تا کلاس Car را معرفی کنیم. +```java +public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts { + + public Car(Map properties) { + super(properties); + } +} +``` +و در نهایت، نحوه ساخت و استفاده از Car را در یک مثال کامل می‌بینید. +```java + public static void main(String[] args) { + LOGGER.info("Constructing parts and car"); + + var wheelProperties = Map.of( + Property.TYPE.toString(), "wheel", + Property.MODEL.toString(), "15C", + Property.PRICE.toString(), 100L); + + var doorProperties = Map.of( + Property.TYPE.toString(), "door", + Property.MODEL.toString(), "Lambo", + Property.PRICE.toString(), 300L); + + var carProperties = Map.of( + Property.MODEL.toString(), "300SL", + Property.PRICE.toString(), 10000L, + Property.PARTS.toString(), List.of(wheelProperties, doorProperties)); + + var car = new Car(carProperties); + + LOGGER.info("Here is our car:"); + LOGGER.info("-> model: {}", car.getModel().orElseThrow()); + LOGGER.info("-> price: {}", car.getPrice().orElseThrow()); + LOGGER.info("-> parts: "); + car.getParts().forEach(p -> LOGGER.info("\t{}/{}/{}", + p.getType().orElse(null), + p.getModel().orElse(null), + p.getPrice().orElse(null)) + ); +} +``` +خروجی برنامه: +``` +07:21:57.391 [main] INFO com.iluwatar.abstractdocument.App -- Constructing parts and car +07:21:57.393 [main] INFO com.iluwatar.abstractdocument.App -- Here is our car: +07:21:57.393 [main] INFO com.iluwatar.abstractdocument.App -- -> model: 300SL +07:21:57.394 [main] INFO com.iluwatar.abstractdocument.App -- -> price: 10000 +07:21:57.394 [main] INFO com.iluwatar.abstractdocument.App -- -> parts: +07:21:57.395 [main] INFO com.iluwatar.abstractdocument.App -- wheel/15C/100 +07:21:57.395 [main] INFO com.iluwatar.abstractdocument.App -- door/Lambo/300 +``` + + ### چه زمانی از الگوی Abstract Document در جاوا استفاده کنیم؟ + +الگوی طراحی Abstract Document به‌ویژه در سناریوهایی مفید است که نیاز به مدیریت انواع مختلفی از اسناد در جاوا وجود دارد که برخی ویژگی‌ها یا رفتارهای مشترک دارند، ولی ویژگی‌ها یا رفتارهای خاص خود را نیز دارند. در ادامه چند سناریوی مناسب برای این الگو آورده شده است: + +* سیستم‌های مدیریت محتوا (CMS): ممکن است انواع مختلفی از محتوا مانند مقاله، تصویر، ویدئو و... وجود داشته باشد. هر نوع محتوا ویژگی‌های مشترکی مثل تاریخ ایجاد، نویسنده و تگ‌ها دارد، ولی همچنین ویژگی‌های خاصی مثل ابعاد تصویر یا مدت‌زمان ویدئو. + +* سیستم‌های فایل: اگر یک سیستم فایل طراحی می‌کنید که باید انواع مختلف فایل مانند اسناد، تصاویر، فایل‌های صوتی و دایرکتوری‌ها را مدیریت کند، این الگو می‌تواند راهی یکپارچه برای دسترسی به ویژگی‌هایی مانند اندازه فایل یا تاریخ ایجاد، فراهم کند و در عین حال ویژگی‌های خاص هر نوع فایل را هم مدیریت کند. + +* سیستم‌های تجارت الکترونیک: یک پلتفرم فروش آنلاین ممکن است محصولات مختلفی داشته باشد مثل محصولات فیزیکی، فایل‌های دیجیتال، و اشتراک‌ها. این محصولات ویژگی‌هایی مثل نام، قیمت و توضیح را به اشتراک می‌گذارند، ولی ویژگی‌های خاصی هم دارند مانند وزن حمل برای محصولات فیزیکی یا لینک دانلود برای دیجیتال‌ها. + +* سیستم‌های سوابق پزشکی: در مراقبت سلامت، پرونده بیماران ممکن است داده‌های مختلفی مثل مشخصات فردی، سوابق پزشکی، نتایج آزمایش‌ها و نسخه‌ها را شامل شود. این الگو می‌تواند ویژگی‌های مشترک مثل شماره بیمار یا تاریخ تولد را مدیریت کند و هم‌زمان ویژگی‌های خاصی مثل نتایج آزمایش یا داروهای تجویزی را هم پوشش دهد. + +* مدیریت پیکربندی: هنگام کار با تنظیمات پیکربندی نرم‌افزار، ممکن است انواع مختلفی از عناصر پیکربندی وجود داشته باشد، هر یک با ویژگی‌های خاص خود. این الگو می‌تواند برای مدیریت این عناصر مفید باشد. + +* پلتفرم‌های آموزشی: سیستم‌های آموزشی ممکن است انواع مختلفی از منابع یادگیری داشته باشند مثل محتوای متنی، ویدیوها، آزمون‌ها و تمرین‌ها. ویژگی‌های مشترکی مثل عنوان، نویسنده و تاریخ انتشار وجود دارد، ولی ویژگی‌های خاصی مانند مدت ویدیو یا مهلت تحویل تمرین نیز ممکن است وجود داشته باشد. + +* ابزارهای مدیریت پروژه: در برنامه‌های مدیریت پروژه، ممکن است انواع مختلفی از وظایف مانند آیتم‌های to-do، milestoneها و issueها داشته باشید. این الگو می‌تواند برای مدیریت ویژگی‌های عمومی مانند نام وظیفه و مسئول آن استفاده شود و در عین حال ویژگی‌های خاص مانند تاریخ milestone یا اولویت issue را نیز پوشش دهد. + +* اسناد ساختار ویژگی‌های متنوع و در حال تحول دارند. + +* افزودن ویژگی‌های جدید به‌صورت پویا یک نیاز رایج است. + +* جداسازی دسترسی به داده از فرمت‌های خاص حیاتی است. + +* نگهداری‌پذیری و انعطاف‌پذیری برای کد اهمیت دارد. + +ایده اصلی پشت الگوی Abstract Document فراهم کردن روشی انعطاف‌پذیر و قابل توسعه برای مدیریت انواع مختلف اسناد یا موجودیت‌ها با ویژگی‌های مشترک و خاص است. با تعریف یک واسط مشترک و پیاده‌سازی آن در انواع مختلف اسناد، می‌توان به شیوه‌ای منظم و یکپارچه برای مدیریت ساختارهای پیچیده داده دست یافت. +### مزایا و معایب الگوی Abstract Document +

+مزایا: + +* انعطاف‌پذیری: پشتیبانی از ساختارهای متنوع اسناد و ویژگی‌ها. + +* قابلیت توسعه: افزودن ویژگی‌های جدید بدون شکستن کد موجود. + +* نگهداری‌پذیری: ارتقاء کد تمیز و قابل تطبیق به‌واسطه جداسازی وظایف. + +* قابلیت استفاده مجدد: نمای دید تایپ‌شده باعث استفاده مجدد از کد برای دسترسی به نوع خاصی از ویژگی می‌شود. + +معایب: + +* پیچیدگی: نیاز به تعریف واسط‌ها و نماها، که باعث اضافه شدن سربار پیاده‌سازی می‌شود. + +* کارایی: ممکن است سربار کمی نسبت به دسترسی مستقیم به داده داشته باشد. +
+ +منابع و اعتبارها + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI) + +* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3yhh525) + +* [Pattern-Oriented Software Architecture Volume 4: A Pattern Language for Distributed Computing (v. 4)] (https://amzn.to/49zRP4R) + +* [Patterns of Enterprise Application Architecture] (https://amzn.to/3WfKBPR) + +* [Abstract Document Pattern (Wikipedia)] (https://en.wikipedia.org/wiki/Abstract_Document_Pattern) + +* [Dealing with Properties (Martin Fowler)] (http://martinfowler.com/apsupp/properties.pdf) diff --git a/localization/fa/abstract-document/etc/abstract-document.png b/localization/fa/abstract-document/etc/abstract-document.png new file mode 100644 index 000000000000..6bc0b29a4e77 Binary files /dev/null and b/localization/fa/abstract-document/etc/abstract-document.png differ diff --git a/localization/fa/active-object/README.md b/localization/fa/active-object/README.md new file mode 100644 index 000000000000..7c105b072aaa --- /dev/null +++ b/localization/fa/active-object/README.md @@ -0,0 +1,220 @@ +--- +title: "الگوی Active Object در جاوا: دستیابی به پردازش ناهمگام کارآمد" +shortTitle: Active Object +description: "با الگوی طراحی Active Object در جاوا آشنا شوید. این راهنما رفتار ناهمگام، هم‌زمانی (concurrency) و مثال‌های کاربردی برای بهبود عملکرد برنامه‌های جاوای شما را پوشش می‌دهد." +category: Concurrency +language: fa +tag: + - Asynchronous + - Decoupling + - Messaging + - Synchronization + - Thread management +--- + +## هدف الگوی طراحی Active Object + +الگوی Active Object روشی مطمئن برای پردازش ناهمگام در جاوا فراهم می‌کند که به پاسخ‌گو بودن برنامه‌ها و مدیریت مؤثر threadها کمک می‌کند. این الگو با محصور کردن وظایف در شیءهایی که هر کدام thread و صف پیام مخصوص خود را دارند، به این هدف می‌رسد. این جداسازی باعث می‌شود thread اصلی پاسخ‌گو باقی بماند و مشکلاتی مانند دست‌کاری مستقیم threadها یا دسترسی به وضعیت مشترک (shared state) به وجود نیاید. + +## توضیح کامل الگوی Active Object با مثال‌های دنیای واقعی + +مثال دنیای واقعی + +> تصور کنید در یک رستوران شلوغ، مشتریان سفارش خود را به گارسون‌ها می‌سپارند. به‌جای آنکه گارسون‌ها خودشان به آشپزخانه بروند و غذا را آماده کنند، سفارش‌ها را روی کاغذهایی می‌نویسند و به یک هماهنگ‌کننده می‌دهند. این هماهنگ‌کننده گروهی از سرآشپزها را مدیریت می‌کند که غذاها را به صورت ناهمگام آماده می‌کنند. هرگاه آشپزی آزاد شود، سفارش بعدی را از صف برمی‌دارد، غذا را آماده می‌کند و پس از آن گارسون را برای سرو غذا مطلع می‌سازد. +> +> در این قیاس، گارسون‌ها نماینده threadهای کلاینت هستند، هماهنگ‌کننده نقش زمان‌بند (scheduler) را ایفا می‌کند، و آشپزها نمایان‌گر اجرای متدها در threadهای جداگانه هستند. این ساختار باعث می‌شود گارسون‌ها بتوانند بدون مسدود شدن توسط فرایند آماده‌سازی غذا، سفارش‌های بیشتری دریافت کنند—درست مانند اینکه الگوی Active Object، فراخوانی متد را از اجرای آن جدا می‌کند تا هم‌زمانی (concurrency) را افزایش دهد. + +به زبان ساده + +> الگوی Active Object، اجرای متد را از فراخوانی آن جدا می‌کند تا در برنامه‌های چندریسمانی (multithreaded)، هم‌زمانی و پاسخ‌گویی بهتری فراهم شود. + +طبق تعریف ویکی‌پدیا + +> الگوی طراحی Active Object اجرای متد را از فراخوانی آن جدا می‌کند، برای شیءهایی که هرکدام thread کنترل مخصوص به خود را دارند. هدف، معرفی هم‌زمانی با استفاده از فراخوانی متد به‌صورت ناهمگام و یک زمان‌بند برای مدیریت درخواست‌ها است. +> +> این الگو شامل شش جزء کلیدی است: +> +> * یک proxy، که رابطی برای کلاینت‌ها با متدهای عمومی فراهم می‌کند. +> * یک interface که درخواست متد برای شیء فعال (active object) را تعریف می‌کند. +> * فهرستی از درخواست‌های معلق از سوی کلاینت‌ها. +> * یک زمان‌بند (scheduler) که تصمیم می‌گیرد کدام درخواست بعدی اجرا شود. +> * پیاده‌سازی متد شیء فعال. +> * یک callback یا متغیر برای اینکه کلاینت نتیجه را دریافت کند. + +نمودار توالی + +![Active Object sequence diagram](./etc/active-object-sequence-diagram.png) + +## مثال برنامه‌نویسی از Active Object در جاوا + +این بخش نحوه عملکرد الگوی Active Object در جاوا را توضیح می‌دهد و کاربرد آن در مدیریت وظایف ناهمگام و کنترل هم‌زمانی را نشان می‌دهد. + +اورک‌ها به دلیل ذات وحشی و غیرقابل مهارشان شناخته می‌شوند. به‌نظر می‌رسد هرکدام thread کنترل مخصوص خود را دارند. برای پیاده‌سازی یک موجود که دارای سازوکار thread مستقل خود باشد و فقط API را در اختیار قرار دهد نه اجرای داخلی را، می‌توان از الگوی Active Object استفاده کرد. + +```java +public abstract class ActiveCreature { + private final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName()); + + private BlockingQueue requests; + + private String name; + + private Thread thread; + + public ActiveCreature(String name) { + this.name = name; + this.requests = new LinkedBlockingQueue(); + thread = new Thread(new Runnable() { + @Override + public void run() { + while (true) { + try { + requests.take().run(); + } catch (InterruptedException e) { + logger.error(e.getMessage()); + } + } + } + } + ); + thread.start(); + } + + public void eat() throws InterruptedException { + requests.put(new Runnable() { + @Override + public void run() { + logger.info("{} is eating!", name()); + logger.info("{} has finished eating!", name()); + } + } + ); + } + + public void roam() throws InterruptedException { + requests.put(new Runnable() { + @Override + public void run() { + logger.info("{} has started to roam the wastelands.", name()); + } + } + ); + } + + public String name() { + return this.name; + } +} +``` + +می‌توان دید هر کلاسی که از ActiveCreature ارث‌بری کند، دارای thread کنترل مختص به خود برای فراخوانی و اجرای متدها خواهد بود. + +برای مثال، کلاس Orc: + +```java +public class Orc extends ActiveCreature { + + public Orc(String name) { + super(name); + } +} +``` +اکنون می‌توان چند موجود مانند orc ایجاد کرد، به آن‌ها دستور داد که بخورند و پرسه بزنند، و آن‌ها این دستورات را در thread مختص به خود اجرا می‌کنند: + +```java +public class App implements Runnable { + + private static final Logger logger = LoggerFactory.getLogger(App.class.getName()); + + private static final int NUM_CREATURES = 3; + + public static void main(String[] args) { + var app = new App(); + app.run(); + } + + @Override + public void run() { + List creatures = new ArrayList<>(); + try { + for (int i = 0; i < NUM_CREATURES; i++) { + creatures.add(new Orc(Orc.class.getSimpleName() + i)); + creatures.get(i).eat(); + creatures.get(i).roam(); + } + Thread.sleep(1000); + } catch (InterruptedException e) { + logger.error(e.getMessage()); + Thread.currentThread().interrupt(); + } finally { + for (int i = 0; i < NUM_CREATURES; i++) { + creatures.get(i).kill(0); + } + } + } +} +``` + +خروجی برنامه: + +``` +09:00:02.501 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 is eating! +09:00:02.501 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 is eating! +09:00:02.501 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 is eating! +09:00:02.504 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 has finished eating! +09:00:02.504 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 has finished eating! +09:00:02.504 [Thread-0] INFO com.iluwatar.activeobject.ActiveCreature -- Orc0 has started to roam in the wastelands. +09:00:02.504 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 has finished eating! +09:00:02.504 [Thread-1] INFO com.iluwatar.activeobject.ActiveCreature -- Orc1 has started to roam in the wastelands. +09:00:02.504 [Thread-2] INFO com.iluwatar.activeobject.ActiveCreature -- Orc2 has started to roam in the wastelands. +``` + +چه زمانی از الگوی Active Object در جاوا استفاده کنیم؟ + +از الگوی Active Object در جاوا استفاده کنید زمانی که: +> * نیاز دارید وظایف ناهمگام را بدون مسدود کردن thread اصلی مدیریت کنید تا عملکرد و پاسخ‌گویی بهتری داشته باشید. +> * نیاز به تعامل ناهمگام با منابع خارجی دارید. +> * می‌خواهید پاسخ‌گویی برنامه را افزایش دهید. +> * نیاز به مدیریت وظایف هم‌زمان به‌صورت ماژولار و قابل نگهداری دارید. + +آموزش‌های Java برای الگوی Active Object +> [Android and Java Concurrency: The Active Object Pattern (Douglas Schmidt)]((https://www.youtube.com/watch?v=Cd8t2u5Qmvc)) + +کاربردهای دنیای واقعی الگوی Active Object در جاوا + +> سیستم‌های معاملات بلادرنگ که درخواست‌ها به‌صورت ناهمگام پردازش می‌شوند. +> که در آن وظایف طولانی در پس‌زمینه اجرا می‌شوند بدون آنکه رابط کاربری را متوقف کنند. +> رابط‌های کاربری گرافیکی (GUI) +> برنامه‌نویسی بازی‌ها برای مدیریت به‌روزرسانی‌های هم‌زمان وضعیت بازی یا محاسبات هوش مصنوعی. + +مزایا و ملاحظات الگوی Active Object + +با مزایا و معایب استفاده از الگوی Active Object در جاوا آشنا شوید؛ از جمله بهبود ایمنی threadها و ملاحظات سربار احتمالی (overhead). + +> مزایا: +> +> * پاسخ‌گویی بهتر thread اصلی. +> * محصورسازی مسائل مربوط به هم‌زمانی درون شیءها. +> * بهبود سازمان‌دهی کد و قابلیت نگهداری. +> * فراهم‌سازی ایمنی در برابر شرایط بحرانی (thread safety) و جلوگیری از مشکلات وضعیت مشترک. + +> معایب: +> +> * سربار اضافی به دلیل ارسال پیام و مدیریت threadها. +> * برای تمام سناریوهای هم‌زمانی مناسب نیست. + +الگوهای طراحی مرتبط در جاوا + +> * [Command](https://java-design-patterns.com/patterns/command/): درخواست را به‌عنوان یک شیء کپسوله می‌کند، مشابه روشی که Active Object فراخوانی متد را کپسوله می‌کند. +> * [Promise](https://java-design-patterns.com/patterns/promise/): راهی برای دریافت نتیجه یک فراخوانی متد ناهمگام فراهم می‌کند؛ اغلب همراه با Active Object استفاده می‌شود. +> * [Proxy](https://java-design-patterns.com/patterns/proxy/): الگوی Active Object می‌تواند از proxy برای مدیریت فراخوانی‌های متد به‌صورت ناهمگام استفاده کند. + +منابع و مراجع + +> * [Design Patterns: Elements of Reusable Object Software](https://amzn.to/3HYqrBE) +> * [Concurrent Programming in Java: Design Principles and Patterns](https://amzn.to/498SRVq) +> * [Java Concurrency in Practice](https://amzn.to/4aRMruW) +> * [Learning Concurrent Programming in Scala](https://amzn.to/3UE07nV) +> * [Pattern Languages of Program Design 3](https://amzn.to/3OI1j61) +> * [Pattern-Oriented Software Architecture Volume 2: Patterns for Concurrent and Networked Objects](https://amzn.to/3UgC24V) + diff --git a/localization/fa/active-object/etc/active-object-sequence-diagram.png b/localization/fa/active-object/etc/active-object-sequence-diagram.png new file mode 100644 index 000000000000..b725d9b07b6d Binary files /dev/null and b/localization/fa/active-object/etc/active-object-sequence-diagram.png differ diff --git a/localization/fa/active-object/etc/active-object.urm.png b/localization/fa/active-object/etc/active-object.urm.png new file mode 100644 index 000000000000..c14f66144ee2 Binary files /dev/null and b/localization/fa/active-object/etc/active-object.urm.png differ diff --git a/localization/fa/active-object/etc/active-object.urm.puml b/localization/fa/active-object/etc/active-object.urm.puml new file mode 100644 index 000000000000..3fc3c8e1e921 --- /dev/null +++ b/localization/fa/active-object/etc/active-object.urm.puml @@ -0,0 +1,25 @@ +@startuml +package com.iluwatar.activeobject { + abstract class ActiveCreature { + - logger : Logger + - name : String + - requests : BlockingQueue + - thread : Thread + + ActiveCreature(name : String) + + eat() + + name() : String + + roam() + } + class App { + - creatures : Integer + - logger : Logger + + App() + + main(args : String[]) {static} + + run() + } + class Orc { + + Orc(name : String) + } +} +Orc --|> ActiveCreature +@enduml \ No newline at end of file diff --git a/localization/fa/factory/README.md b/localization/fa/factory/README.md new file mode 100644 index 000000000000..db41813464e3 --- /dev/null +++ b/localization/fa/factory/README.md @@ -0,0 +1,155 @@ +--- +title: "الگوی factory در جاوا: ساده‌سازی ایجاد اشیاء" +shortTitle: factory +description: "الگوی طراحی factory در جاوا را با مثال‌ها و توضیحات دقیق بیاموزید. یاد بگیرید چگونه با استفاده از الگوی factory کدی انعطاف‌پذیر و مقیاس‌پذیر ایجاد کنید. مناسب برای توسعه‌دهندگانی که به دنبال بهبود مهارت‌های طراحی شیءگرا هستند." +category: structural +language: fa +tag: + - Abstraction + - Encapsulation + - Gang of Four + - Instantiation + - Polymorphism +--- + +## هدف از الگوی طراحی factory + +الگوی طراحی factory در جاوا یک الگوی ساختاری است که یک رابط برای ایجاد یک شیء تعریف می‌کند اما به زیرکلاس‌ها اجازه می‌دهد نوع اشیائی را که ایجاد خواهند شد تغییر دهند. این الگو انعطاف‌پذیری و مقیاس‌پذیری را در کد شما ترویج می‌دهد. + +## توضیح دقیق الگوی factory با مثال‌های دنیای واقعی + +### مثال دنیای واقعی + +> تصور کنید در یک نانوایی انواع مختلف کیک‌ها با استفاده از الگوی طراحی factory ساخته می‌شوند. `CakeFactory` فرآیند ایجاد را مدیریت می‌کند و امکان افزودن آسان انواع جدید کیک‌ها را بدون تغییر در فرآیند اصلی فراهم می‌کند. `CakeFactory` می‌تواند انواع مختلفی از کیک‌ها مانند کیک شکلاتی، کیک وانیلی و کیک توت‌فرنگی تولید کند. به جای اینکه کارکنان نانوایی به صورت دستی مواد اولیه را انتخاب کنند و دستورالعمل‌های خاصی را برای هر نوع کیک دنبال کنند، از `CakeFactory` برای مدیریت فرآیند استفاده می‌کنند. مشتری فقط نوع کیک را درخواست می‌کند و `CakeFactory` مواد اولیه و دستورالعمل مناسب را تعیین کرده و نوع خاصی از کیک را ایجاد می‌کند. این تنظیم به نانوایی اجازه می‌دهد تا انواع جدید کیک‌ها را به راحتی اضافه کند بدون اینکه فرآیند اصلی تغییر کند، که این امر انعطاف‌پذیری و مقیاس‌پذیری را ترویج می‌دهد. + +### تعریف ویکی‌پدیا + +> الگوی factory یک شیء برای ایجاد اشیاء دیگر است – به طور رسمی، factory یک تابع یا متدی است که اشیاء با نمونه‌ها یا کلاس‌های مختلف را بازمی‌گرداند. + +### نمودار توالی + +![نمودار توالی factory](./etc/factory-sequence-diagram.png) + +## مثال برنامه‌نویسی از الگوی factory در جاوا + +تصور کنید یک کیمیاگر قصد دارد سکه‌هایی تولید کند. کیمیاگر باید بتواند هم سکه‌های طلا و هم سکه‌های مسی ایجاد کند و تغییر بین آن‌ها باید بدون تغییر در کد موجود امکان‌پذیر باشد. الگوی factory این امکان را فراهم می‌کند با ارائه یک متد ایجاد استاتیک که می‌توان آن را با پارامترهای مرتبط فراخوانی کرد. + +در جاوا، می‌توانید الگوی factory را با تعریف یک رابط `Coin` و پیاده‌سازی‌های آن `GoldCoin` و `CopperCoin` پیاده‌سازی کنید. کلاس `CoinFactory` یک متد استاتیک `getCoin` ارائه می‌دهد تا اشیاء سکه را بر اساس نوع ایجاد کند. + +```java +public interface Coin { + String getDescription(); +} +``` + +```java +public class GoldCoin implements Coin { + + static final String DESCRIPTION = "This is a gold coin."; + + @Override + public String getDescription() { + return DESCRIPTION; + } +} +``` + +```java +public class CopperCoin implements Coin { + + static final String DESCRIPTION = "This is a copper coin."; + + @Override + public String getDescription() { + return DESCRIPTION; + } +} +``` + +کد زیر انواع سکه‌هایی که پشتیبانی می‌شوند (`GoldCoin` و `CopperCoin`) را نشان می‌دهد. + +```java +@RequiredArgsConstructor +@Getter +public enum CoinType { + + COPPER(CopperCoin::new), + GOLD(GoldCoin::new); + + private final Supplier constructor; +} +``` + +سپس متد استاتیک `getCoin` برای ایجاد اشیاء سکه در کلاس factory `CoinFactory` کپسوله شده است. + +```java +public class CoinFactory { + + public static Coin getCoin(CoinType type) { + return type.getConstructor().get(); + } +} +``` + +اکنون، در کد کلاینت، می‌توانیم انواع مختلفی از سکه‌ها را با استفاده از کلاس factory تولید کنیم. + +```java +public static void main(String[] args) { + LOGGER.info("The alchemist begins his work."); + var coin1 = CoinFactory.getCoin(CoinType.COPPER); + var coin2 = CoinFactory.getCoin(CoinType.GOLD); + LOGGER.info(coin1.getDescription()); + LOGGER.info(coin2.getDescription()); +} +``` + +خروجی برنامه: + +``` +06:19:53.530 [main] INFO com.iluwatar.factory.App -- The alchemist begins his work. +06:19:53.533 [main] INFO com.iluwatar.factory.App -- This is a copper coin. +06:19:53.533 [main] INFO com.iluwatar.factory.App -- This is a gold coin. +``` + +## زمان استفاده از الگوی factory در جاوا + +* از الگوی طراحی factory در جاوا زمانی استفاده کنید که کلاس از قبل نوع دقیق و وابستگی‌های اشیائی که نیاز به ایجاد آن دارد را نمی‌داند. +* زمانی که یک متد یکی از چندین کلاس ممکن که یک کلاس والد مشترک دارند را بازمی‌گرداند و می‌خواهد منطق انتخاب شیء را کپسوله کند. +* این الگو معمولاً هنگام طراحی فریم‌ورک‌ها یا کتابخانه‌ها برای ارائه بهترین انعطاف‌پذیری و جداسازی از انواع کلاس‌های خاص استفاده می‌شود. + +## کاربردهای دنیای واقعی الگوی factory در جاوا + +> * [java.util.Calendar#getInstance()](https://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#getInstance--) +> * [java.util.ResourceBundle#getBundle()](https://docs.oracle.com/javase/8/docs/api/java/util/ResourceBundle.html#getBundle-java.lang.String-) +> * [java.text.NumberFormat#getInstance()](https://docs.oracle.com/javase/8/docs/api/java/text/NumberFormat.html#getInstance--) +> * [java.nio.charset.Charset#forName()](https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html#forName-java.lang.String-) +> * این مورد [java.net.URLStreamHandlerFactory#createURLStreamHandler(String)](https://docs.oracle.com/javase/8/docs/api/java/net/URLStreamHandlerFactory.html) اشیاء singleton مختلف را بر اساس یک پروتکل بازمی‌گرداند +> * [java.util.EnumSet#of()](https://docs.oracle.com/javase/8/docs/api/java/util/EnumSet.html#of(E)) +> * [javax.xml.bind.JAXBContext#createMarshaller()](https://docs.oracle.com/javase/8/docs/api/javax/xml/bind/JAXBContext.html#createMarshaller--) و متدهای مشابه دیگر. +> +> * کتابخانه‌ی JavaFX از الگوهای factory برای ایجاد کنترل‌های مختلف رابط کاربری متناسب با نیازهای محیط کاربر استفاده می‌کند. + +## مزایا و معایب الگوی factory + +### مزایا: + +> * پیاده‌سازی الگوی factory در برنامه جاوای شما، وابستگی بین پیاده‌سازی و کلاس‌هایی که استفاده می‌کند را کاهش می‌دهد. +> * از [اصل Open/Closed](https://java-design-patterns.com/principles/#open-closed-principle) پشتیبانی می‌کند، زیرا سیستم می‌تواند انواع جدیدی را بدون تغییر کد موجود معرفی کند. + +### معایب: + +> * کد می‌تواند به دلیل معرفی چندین کلاس اضافی پیچیده‌تر شود. +> * استفاده بیش از حد می‌تواند کد را کمتر خوانا کند اگر پیچیدگی ایجاد اشیاء کم یا غیرضروری باشد. + +## الگوهای طراحی مرتبط با جاوا + +> * الگوی [Abstract Factory](https://java-design-patterns.com/patterns/abstract-factory/): می‌توان آن را نوعی factory در نظر گرفت که با گروهی از محصولات کار می‌کند. +> * الگوی [Singleton](https://java-design-patterns.com/patterns/singleton/): اغلب همراه با factory استفاده می‌شود تا اطمینان حاصل شود که یک کلاس تنها یک نمونه دارد. +> * الگوی [Builder](https://java-design-patterns.com/patterns/builder/): ساخت یک شیء پیچیده را از نمایش آن جدا می‌کند، مشابه نحوه‌ای که factoryها مدیریت نمونه‌سازی را انجام می‌دهند. +> * الگوی [Factory Kit](https://java-design-patterns.com/patterns/factory-kit/): یک factory از محتوای غیرقابل تغییر با رابط‌های builder و factory جداگانه است. + +## منابع و اعتبارات + +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0Rk5y) +* [Effective Java](https://amzn.to/4cGk2Jz) +* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/3UpTLrG) diff --git a/localization/fa/factory/etc/factory-sequence-diagram.png b/localization/fa/factory/etc/factory-sequence-diagram.png new file mode 100644 index 000000000000..260bea92f247 Binary files /dev/null and b/localization/fa/factory/etc/factory-sequence-diagram.png differ diff --git a/monolithic-architecture/README.md b/monolithic-architecture/README.md index 6704cee7c24a..6bb63e04fbfe 100644 --- a/monolithic-architecture/README.md +++ b/monolithic-architecture/README.md @@ -1,50 +1,52 @@ --- -title: "Monolithic Ecommerce App: A Cohesive Application Model" -shortTitle: Monolithic Ecommerce -description: "Explore the Monolithic Ecommerce application structure, its design intent, benefits, limitations, and real-world applications. Understand its simplicity and practical use cases." +title: "Monolithic Architecture in Java: A Cohesive Application Model" +shortTitle: Monolithic Architecture +description: "Explore the Monolithic Architecture application structure, its design intent, benefits, limitations, and real-world applications. Understand its simplicity and practical use cases." category: Architectural language: en tag: - - Cohesion + - Architecture + - Cohesion + - Encapsulation + - Layered architecture + - Modularity --- -## Monolithic-Ecommerce App -* A Monolithic Ecommerce example to showcase Monolithic Architecture +## Also known as + +* Single-tier architecture +* Monolith ## The Intent of Monolithic Design pattern -> the Monolithic Design Pattern structures an application as a single, cohesive unit where all components—such as business logic, user interface, and data access are tightly integrated and operate as part of a single executable. + +Encapsulate all the functionality of an application within a single, cohesive codebase. ## Detailed Explanation of the Monolithic Architecture -Real-world Example -> A traditional E-commerce website is the most straightforward example for a monolithic application as it is comprised of a catalogue of products, orders to be made, shopping carts, and payment processes that are all inseperable of each other. - -In Plain words ->The monolithic design pattern structures an application as a single unified unit, where all components are tightly coupled and run within a single process. - -GeeksforGeeks states -> Monolithic architecture, a traditional approach in system design, which contains all application components into a single codebase. This unified structure simplifies development and deployment processes, offering ease of management and tight integration. However, because of its rigidity, it is difficult to scale and maintain, which makes it difficult to adjust to changing needs. - -Why use MVC for a Monolithic Application ? ->The Model-View-Controller (MVC) pattern is not inherently tied to microservices or distributed systems. It's a software design pattern that organizes the codebase by separating concerns into three distinct layers: ->* Model ->* View ->* Controller -> -> this also helps maintain the principles of a Monolithic Architecture which are: -> -> Simple to ->* Develop ->* Test ->* Deploy ->* Scale -> - -Architecture diagram - -![Model-View-Controller Architecture Diagram](./etc/mvc-architecture-diagram.png) - -## We can clearly see that this is a Monolithic application through the main class -This is a simplified version of the main application that shows the main interaction point with the CLI and how a user is registered + +Real-world example + +> An analogous real-world example of the Monolithic Architecture pattern is a department store. Just like a monolithic Java application, a department store hosts all product categories, sales, storage, and customer services within a single, large building. It simplifies shopping by consolidating everything under one roof, making operations straightforward and easy to manage. However, expanding or rearranging specific departments becomes increasingly challenging over time, similar to how scaling individual functionalities within a monolithic system can become complex and cumbersome. + +In plain words + +> The monolithic design pattern structures an application as a single unified unit, where all components are tightly coupled and run within a single process. + +Wikipedia says + +> In software engineering, a monolithic application is a single unified software application that is self-contained and independent of other applications, but typically lacks flexibility. There are advantages and disadvantages of building applications in a monolithic style of software architecture, depending on requirements. Monolith applications are relatively simple and have a low cost but their shortcomings are lack of elasticity, fault tolerance and scalability. + +Mind map + +![Monolithic Architecture Mind Map](./etc/monolithic-architecture-mind-map.png) + +Flowchart + +![Monolithic Architecture Flowchart](./etc/monolithic-architecture-flowchart.png) + +## Programmatic Example of Monolithic Architecture in Java + +This is a simplified version of the main application, demonstrating how a monolithic architecture can be implemented. Here, all the essential services—such as user management (`UserCon`), product management (`ProductCon`), and order processing (`OrderCon`) — are tightly integrated within a single executable Java application. The CLI provides a straightforward user interaction point where operations like user registration, adding products, and placing orders are handled. + ```java @SpringBootApplication public class EcommerceApp implements CommandLineRunner { @@ -105,47 +107,46 @@ public class EcommerceApp implements CommandLineRunner { } ``` -### We can clearly reach the conclusion that all of these classes reside under the same module and are essential for each other's functionality, this is supported by the presence of all relevant classes as parts of the main application class. - -## When should you resort to a Monolithic Architecture ? ->* An enterprise Starting off with a relatively small team ->* Simplicity is the most important factor of the project ->* Maintaining less entry points to the system is cruical ->* Prototyping ideas -> -## Pros & Cons of using Monolithic Architecture ->### Pros: ->* Simple Development: Easy to develop and deploy. ->* Unified Codebase: All code in one place, simplifying debugging. ->* Better Performance: No inter-service communication overhead. ->* Lower Costs: Minimal infrastructure and tooling requirements. ->* Ease of Testing: Single application makes end-to-end testing straightforward. -> * This is also assisted by the MVC structure employed in this example. ->### Cons: ->* Scalability Issues: Cannot scale individual components. ->* Tight Coupling: Changes in one area may impact the whole system. ->* Deployment Risks: A single failure can crash the entire application. ->* Complex Maintenance: Harder to manage as the codebase grows. ->* Limited Flexibility: Difficult to adopt new technologies for specific parts. - -## Real-World Applications of Monolithic architecture Pattern in Java ->* E-Commerce Platforms ->* Content Management Systems (CMS) ->* Banking and Financial Systems ->* Enterprise Resource Planning (ERP) Systems ->* Retail Point of Sale (POS) Systems + +In this example, the core business functionalities are closely interconnected, sharing common resources and residing within the same codebase. This approach simplifies initial application development, testing, and deployment, as each component can easily access the others directly without the overhead of inter-service communication. However, as the application grows, scaling individual components independently becomes more challenging, highlighting the key trade-off inherent in the monolithic architecture. + +## When to Use the Monolithic Architecture in Java + +* Suitable for small to medium-sized applications where simplicity, cohesive development, and ease of deployment outweigh scalability and flexibility requirements. +* Applicable in contexts where initial rapid development and ease of testing are prioritized. + +## Real-World Applications of Monolithic Architecture in Java + +* Early-stage Java web applications developed using frameworks like Spring MVC, Java EE (Servlets/JSP), or frameworks like Play. +* Traditional Java enterprise applications packaged and deployed as WAR or EAR files on application servers such as Apache Tomcat, Jetty, or WildFly. +* Standalone Java applications and desktop applications packaged as single executable JAR files. + +## Benefits and Trade-offs of Monolithic Architecture + +Benefits: + +* Simpler to develop, test, and deploy as the application is a single unit. +* Easier debugging and performance monitoring due to the single unified runtime. +* Typically faster initial development and straightforward management of dependencies. + +Trade-offs: + +* Poor scalability and potential performance bottlenecks as the application grows. +* Limited modularity, leading to increased complexity and harder maintainability over time. +* Slow deployments and updates due to a single large codebase. +* Difficult to scale individual functionalities independently. + +## Related Patterns + +* Microservices Architecture: Breaks down a monolithic application into independently deployable services, directly addressing the scalability and maintainability limitations of monoliths. +* [Layered Architecture](https://java-design-patterns.com/patterns/layered-architecture/): Often used internally within monoliths to provide clear separation between presentation, business logic, and persistence layers. ## References ->* [GeeksforGeeks](https://www.geeksforgeeks.org/monolithic-architecture-system-design/) ->* [Wikipedia](https://en.wikipedia.org/wiki/Monolithic_application) ->* [vFunction](https://vfunction.com/blog/what-is-monolithic-application/#:~:text=A%20traditional%20e%2Dcommerce%20platform,inseparable%20components%20of%20the%20system.) Blog post ->* [Microservices.io](https://microservices.io/patterns/monolithic.html) ->* [IBM](https://www.ibm.com/think/topics/monolithic-architecture) ->#### References used to create the code ->* [Mockito](https://site.mockito.org/) -Testing ->* [Junit](https://junit.org/junit5/docs/current/user-guide/) -Testing ->* [Springboot](https://docs.spring.io/spring-boot/index.html) -Web Application Initiation (implemented but not utilized in this example) ->* [Sprint Data Jpa](https://docs.spring.io/spring-data/jpa/reference/index.html) -Database connection ->* [Lombok](https://projectlombok.org/) -Simplifying Classes ->* [Log4j](https://logging.apache.org/log4j/2.x/index.html) -Capturing Logs ->* [H2 Databse](https://www.h2database.com/html/tutorial.html) -Efficient, Simple, Dynamic Databse \ No newline at end of file + +* [Building Microservices](https://amzn.to/3UACtrU) +* [Fundamentals of Software Architecture: An Engineering Approach](https://amzn.to/4cx4A2N) +* [Monolithic Architecture - System Design (GeeksforGeeks)](https://www.geeksforgeeks.org/monolithic-architecture-system-design/) +* [Monolithic Application (Wikipedia)](https://en.wikipedia.org/wiki/Monolithic_application) +* [Pattern: Monolithic Architecture (Microservices.io)](https://microservices.io/patterns/monolithic.html) +* [Patterns of Enterprise Application Architecture](https://amzn.to/3WfKBPR) +* [What Is Monolithic Architecture? (IBM)](https://www.ibm.com/think/topics/monolithic-architecture) diff --git a/monolithic-architecture/etc/monolithic-architecture-flowchart.png b/monolithic-architecture/etc/monolithic-architecture-flowchart.png new file mode 100644 index 000000000000..179faaf7909b Binary files /dev/null and b/monolithic-architecture/etc/monolithic-architecture-flowchart.png differ diff --git a/monolithic-architecture/etc/monolithic-architecture-mind-map.png b/monolithic-architecture/etc/monolithic-architecture-mind-map.png new file mode 100644 index 000000000000..cf955379806a Binary files /dev/null and b/monolithic-architecture/etc/monolithic-architecture-mind-map.png differ diff --git a/monolithic-architecture/src/test/java/com/iluwatar/monolithic/MonolithicAppTest.java b/monolithic-architecture/src/test/java/com/iluwatar/monolithic/MonolithicAppTest.java index 1f31157325e0..967b86e7080f 100644 --- a/monolithic-architecture/src/test/java/com/iluwatar/monolithic/MonolithicAppTest.java +++ b/monolithic-architecture/src/test/java/com/iluwatar/monolithic/MonolithicAppTest.java @@ -95,11 +95,7 @@ void testPlaceOrderUserNotFound() { new OrderController(mockOrderRepo, mockUserRepository, mockProductRepository); Exception exception = - assertThrows( - NonExistentUserException.class, - () -> { - orderCon.placeOrder(1L, 1L, 5); - }); + assertThrows(NonExistentUserException.class, () -> orderCon.placeOrder(1L, 1L, 5)); assertEquals("User with ID 1 not found", exception.getMessage()); } @@ -119,11 +115,7 @@ void testPlaceOrderProductNotFound() { new OrderController(mockOrderRepository, mockUserRepository, mockProductRepository); Exception exception = - assertThrows( - NonExistentProductException.class, - () -> { - orderCon.placeOrder(1L, 1L, 5); - }); + assertThrows(NonExistentProductException.class, () -> orderCon.placeOrder(1L, 1L, 5)); assertEquals("Product with ID 1 not found", exception.getMessage()); } @@ -196,11 +188,7 @@ void testPlaceOrderInsufficientStock() { new OrderController(mockOrderRepository, mockUserRepository, mockProductRepository); Exception exception = - assertThrows( - InsufficientStockException.class, - () -> { - orderCon.placeOrder(1L, 1L, 5); - }); + assertThrows(InsufficientStockException.class, () -> orderCon.placeOrder(1L, 1L, 5)); assertEquals("Not enough stock for product 1", exception.getMessage()); } diff --git a/pom.xml b/pom.xml index ac8d0a98265c..20f468706502 100644 --- a/pom.xml +++ b/pom.xml @@ -63,7 +63,7 @@ 3.14.0 - 5.0.0.4389 + 5.1.0.4751 https://sonarcloud.io iluwatar iluwatar_java-design-patterns @@ -71,6 +71,7 @@ Java Design Patterns + abstract-document abstract-factory active-object @@ -243,6 +244,8 @@ virtual-proxy visitor backpressure + actor-model + @@ -316,12 +319,6 @@ ${junit.version} test - - 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 -![pub-sub](./etc/pub-sub.png) +Sequence diagram + +![Publish-Subscribe sequence diagram](./etc/publish-subscribe-sequence-diagram.png) ## 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 +![Table Inheritance Pattern Mind Map](./etc/table-inheritance-mind-map.png) ## 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(); }