sentimentSupplier) {
+ this.sentimentSupplier = sentimentSupplier;
+ }
+
+ public RealSentimentAnalysisServer() {
+ this(() -> new Random().nextInt(3));
+ }
+
+ @Override
+ public String analyzeSentiment(String text) {
+ int sentiment = sentimentSupplier.get();
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ return sentiment == 0 ? "Positive" : sentiment == 1 ? "Negative" : "Neutral";
+ }
+}
+```
+The stub implementation simulates the real sentiment analysis class and provides a deterministic output
+for a given input. Additionally, its runtime is almost zero.
+```java
+public class StubSentimentAnalysisServer implements SentimentAnalysisServer {
+
+ @Override
+ public String analyzeSentiment(String text) {
+ if (text.toLowerCase().contains("good")) {
+ return "Positive";
+ }
+ else if (text.toLowerCase().contains("bad")) {
+ return "Negative";
+ }
+ else {
+ return "Neutral";
+ }
+ }
+}
+
+```
+Here is the main function of the App class (entry point to the program)
+```java
+@Slf4j
+ public static void main(String[] args) {
+ LOGGER.info("Setting up the real sentiment analysis server.");
+ RealSentimentAnalysisServer realSentimentAnalysisServer = new RealSentimentAnalysisServer();
+ String text = "This movie is soso";
+ LOGGER.info("Analyzing input: {}", text);
+ String sentiment = realSentimentAnalysisServer.analyzeSentiment(text);
+ LOGGER.info("The sentiment is: {}", sentiment);
+
+ LOGGER.info("Setting up the stub sentiment analysis server.");
+ StubSentimentAnalysisServer stubSentimentAnalysisServer = new StubSentimentAnalysisServer();
+ text = "This movie is so bad";
+ LOGGER.info("Analyzing input: {}", text);
+ sentiment = stubSentimentAnalysisServer.analyzeSentiment(text);
+ LOGGER.info("The sentiment is: {}", sentiment);
+ }
+```
+## When to Use the Service Stub Pattern in Java
+
+Use the Service Stub pattern when:
+
+* 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.
+
+## 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.
+
+## Benefits and Trade-offs of Service Stub Pattern
+
+Benefits:
+
+* Reduces dependencies.
+* Provides predictable behavior.
+* Speeds up testing.
+
+Trade-offs:
+
+* Requires maintaining stub logic.
+* May not fully represent real service behavior.
+
+## Related Java Design Patterns
+
+* [Proxy](https://java-design-patterns.com/patterns/proxy/)
+* [Strategy](https://java-design-patterns.com/patterns/strategy/)
+
+## References and Credits
+
+* [Martin Fowler: Test Stubs](https://martinfowler.com/articles/mocksArentStubs.html)
diff --git a/service-stub/pom.xml b/service-stub/pom.xml
new file mode 100644
index 000000000000..d6aad385ba45
--- /dev/null
+++ b/service-stub/pom.xml
@@ -0,0 +1,27 @@
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+
+ service-stub
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+
+
\ No newline at end of file
diff --git a/service-stub/src/main/java/com/iluwatar/servicestub/App.java b/service-stub/src/main/java/com/iluwatar/servicestub/App.java
new file mode 100644
index 000000000000..5521af080ce3
--- /dev/null
+++ b/service-stub/src/main/java/com/iluwatar/servicestub/App.java
@@ -0,0 +1,45 @@
+package com.iluwatar.servicestub;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * A Service Stub is a dummy implementation of an external service used during development or
+ * testing. The purpose is to provide a lightweight "stub" when the real service may not always be
+ * available (or too slow to use during testing).
+ *
+ * This implementation simulates a simple sentiment analysis program, where a text is analyzed to
+ * deduce whether it is a positive, negative or neutral sentiment. The stub returns a response based
+ * on whether the analyzed text contains the words "good" or "bad", not accounting for stopwords or
+ * the underlying semantic of the text.
+ *
+ *
The "real" sentiment analysis class simulates the processing time for the request by pausing
+ * the execution of the thread for 5 seconds. In the stub sentiment analysis class the response is
+ * immediate. In addition, the stub returns a deterministic output with regard to the input. This
+ * is extra useful for testing purposes.
+ */
+
+
+@Slf4j
+public class App {
+ /**
+ * Program entry point.
+ *
+ * @param args command line args
+ */
+ public static void main(String[] args) {
+ LOGGER.info("Setting up the real sentiment analysis server.");
+ RealSentimentAnalysisServer realSentimentAnalysisServer = new RealSentimentAnalysisServer();
+ String text = "This movie is soso";
+ LOGGER.info("Analyzing input: {}", text);
+ String sentiment = realSentimentAnalysisServer.analyzeSentiment(text);
+ LOGGER.info("The sentiment is: {}", sentiment);
+
+ LOGGER.info("Setting up the stub sentiment analysis server.");
+ StubSentimentAnalysisServer stubSentimentAnalysisServer = new StubSentimentAnalysisServer();
+ text = "This movie is so bad";
+ LOGGER.info("Analyzing input: {}", text);
+ sentiment = stubSentimentAnalysisServer.analyzeSentiment(text);
+ LOGGER.info("The sentiment is: {}", sentiment);
+
+ }
+}
diff --git a/service-stub/src/main/java/com/iluwatar/servicestub/RealSentimentAnalysisServer.java b/service-stub/src/main/java/com/iluwatar/servicestub/RealSentimentAnalysisServer.java
new file mode 100644
index 000000000000..cd0175972fda
--- /dev/null
+++ b/service-stub/src/main/java/com/iluwatar/servicestub/RealSentimentAnalysisServer.java
@@ -0,0 +1,45 @@
+package com.iluwatar.servicestub;
+
+import java.util.Random;
+import java.util.function.Supplier;
+
+/**
+ * Real implementation of SentimentAnalysisServer.
+ * Simulates random sentiment classification with processing delay.
+ */
+
+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)
+ */
+
+ private final Supplier sentimentSupplier;
+
+ // Constructor
+ public RealSentimentAnalysisServer(Supplier sentimentSupplier) {
+ this.sentimentSupplier = sentimentSupplier;
+ }
+
+ @SuppressWarnings("java:S2245") // Safe use: Randomness is for simulation/testing only
+ public RealSentimentAnalysisServer() {
+ this(() -> new Random().nextInt(3));
+ }
+
+ @Override
+ public String analyzeSentiment(String text) {
+ int sentiment = sentimentSupplier.get();
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ return sentiment == 0 ? "Positive" : sentiment == 1 ? "Negative" : "Neutral";
+ }
+}
+
diff --git a/service-stub/src/main/java/com/iluwatar/servicestub/SentimentAnalysisServer.java b/service-stub/src/main/java/com/iluwatar/servicestub/SentimentAnalysisServer.java
new file mode 100644
index 000000000000..174f81f4298c
--- /dev/null
+++ b/service-stub/src/main/java/com/iluwatar/servicestub/SentimentAnalysisServer.java
@@ -0,0 +1,15 @@
+package com.iluwatar.servicestub;
+
+/**
+ * Sentiment analysis server interface to be implemented by sentiment analysis services.
+ */
+
+public interface SentimentAnalysisServer {
+ /**
+ * Analyzes the sentiment of the input text and returns the result.
+ *
+ * @param text the input text to analyze
+ * @return sentiment classification result
+ */
+ String analyzeSentiment(String text);
+}
diff --git a/service-stub/src/main/java/com/iluwatar/servicestub/StubSentimentAnalysisServer.java b/service-stub/src/main/java/com/iluwatar/servicestub/StubSentimentAnalysisServer.java
new file mode 100644
index 000000000000..87f5d33e2ed8
--- /dev/null
+++ b/service-stub/src/main/java/com/iluwatar/servicestub/StubSentimentAnalysisServer.java
@@ -0,0 +1,27 @@
+package com.iluwatar.servicestub;
+
+/**
+ * Stub implementation of SentimentAnalysisServer.
+ * Returns deterministic sentiment based on input keywords.
+ */
+
+public class StubSentimentAnalysisServer implements SentimentAnalysisServer {
+
+ /**
+ * Fake sentiment analyzer, always returns "Positive" if input string contains the word "good",
+ * "Negative" if the string contains "bad" and "Neutral" otherwise.
+ *
+ * @param text the input string to analyze
+ * @return sentiment classification result (Positive, Negative, or Neutral)
+ */
+ @Override
+ public String analyzeSentiment(String text) {
+ if (text.toLowerCase().contains("good")) {
+ return "Positive";
+ } else if (text.toLowerCase().contains("bad")) {
+ return "Negative";
+ } else {
+ return "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
new file mode 100644
index 000000000000..5f91eb8bf75d
--- /dev/null
+++ b/service-stub/src/test/java/com/iluwatar/servicestub/AppTest.java
@@ -0,0 +1,11 @@
+package com.iluwatar.servicestub;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+public class AppTest {
+ @Test
+ void shouldExecuteWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/service-stub/src/test/java/com/iluwatar/servicestub/RealSentimentAnalysisServerTest.java b/service-stub/src/test/java/com/iluwatar/servicestub/RealSentimentAnalysisServerTest.java
new file mode 100644
index 000000000000..6a7c8557093e
--- /dev/null
+++ b/service-stub/src/test/java/com/iluwatar/servicestub/RealSentimentAnalysisServerTest.java
@@ -0,0 +1,26 @@
+package com.iluwatar.servicestub;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+
+class RealSentimentAnalysisServerTest {
+
+ @Test
+ void testPositiveSentiment() {
+ RealSentimentAnalysisServer server = new RealSentimentAnalysisServer(() -> 0);
+ assertEquals("Positive", server.analyzeSentiment("Test"));
+ }
+
+ @Test
+ void testNegativeSentiment() {
+ RealSentimentAnalysisServer server = new RealSentimentAnalysisServer(() -> 1);
+ assertEquals("Negative", server.analyzeSentiment("Test"));
+ }
+
+ @Test
+ void testNeutralSentiment() {
+ RealSentimentAnalysisServer server = new RealSentimentAnalysisServer(() -> 2);
+ assertEquals("Neutral", server.analyzeSentiment("Test"));
+ }
+
+}
diff --git a/service-stub/src/test/java/com/iluwatar/servicestub/StubSentimentAnalysisServerTest.java b/service-stub/src/test/java/com/iluwatar/servicestub/StubSentimentAnalysisServerTest.java
new file mode 100644
index 000000000000..3c8ae96829a6
--- /dev/null
+++ b/service-stub/src/test/java/com/iluwatar/servicestub/StubSentimentAnalysisServerTest.java
@@ -0,0 +1,27 @@
+package com.iluwatar.servicestub;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+
+class StubSentimentAnalysisServerTest {
+
+ private final StubSentimentAnalysisServer stub = new StubSentimentAnalysisServer();
+
+ @Test
+ void testPositiveSentiment() {
+ String result = stub.analyzeSentiment("This is a good product");
+ assertEquals("Positive", result);
+ }
+
+ @Test
+ void testNegativeSentiment() {
+ String result = stub.analyzeSentiment("This is a bad product");
+ assertEquals("Negative", result);
+ }
+
+ @Test
+ void testNeutralSentiment() {
+ String result = stub.analyzeSentiment("This product is average");
+ assertEquals("Neutral", result);
+ }
+}