diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/App.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/App.java
index c3465d801..302930585 100644
--- a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/App.java
+++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/App.java
@@ -36,17 +36,18 @@ import org.slf4j.LoggerFactory;
* operational again, so that we can use it
*
*
- * In this example, the circuit breaker pattern is demonstrated by using two services: {@link
- * MonitoringService} and {@link DelayedService}. The monitoring service is responsible for calling
- * two services: a local service and a remote service {@link DelayedService} , and by using the
- * circuit breaker construction we ensure that if the call to remote service is going to fail, we
- * are going to save our resources and not make the function call at all, by wrapping our call to
- * the remote service in the circuit breaker object.
+ * In this example, the circuit breaker pattern is demonstrated by using three services: {@link
+ * DelayedRemoteService}, {@link QuickRemoteService} and {@link MonitoringService}. The monitoring
+ * service is responsible for calling three services: a local service, a quick remove service
+ * {@link QuickRemoteService} and a delayed remote service {@link DelayedRemoteService} , and by
+ * using the circuit breaker construction we ensure that if the call to remote service is going to
+ * fail, we are going to save our resources and not make the function call at all, by wrapping our
+ * call to the remote services in the {@link DefaultCircuitBreaker} implementation object.
*
*
- * This works as follows: The {@link CircuitBreaker} object can be in one of three states:
- * Open, Closed and Half-Open, which represents the real world circuits. If the
- * state is closed (initial), we assume everything is alright and perform the function call.
+ * This works as follows: The {@link DefaultCircuitBreaker} object can be in one of three states:
+ * Open, Closed and Half-Open, which represents the real world circuits. If
+ * the state is closed (initial), we assume everything is alright and perform the function call.
* However, every time the call fails, we note it and once it crosses a threshold, we set the state
* to Open, preventing any further calls to the remote server. Then, after a certain retry period
* (during which we expect thee service to recover), we make another call to the remote server and
@@ -63,22 +64,50 @@ public class App {
*
* @param args command line args
*/
- @SuppressWarnings("squid:S2189")
public static void main(String[] args) {
- //Create an object of monitoring service which makes both local and remote calls
- var obj = new MonitoringService();
- //Set the circuit Breaker parameters
- var circuitBreaker = new CircuitBreaker(3000, 1, 2000 * 1000 * 1000);
+
var serverStartTime = System.nanoTime();
- while (true) {
- LOGGER.info(obj.localResourceResponse());
- LOGGER.info(obj.remoteResourceResponse(circuitBreaker, serverStartTime));
- LOGGER.info(circuitBreaker.getState());
- try {
- Thread.sleep(5 * 1000);
- } catch (InterruptedException e) {
- LOGGER.error(e.getMessage());
- }
+
+ var delayedService = new DelayedRemoteService(serverStartTime, 5);
+ var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000, 2,
+ 2000 * 1000 * 1000);
+
+ var quickService = new QuickRemoteService();
+ var quickServiceCircuitBreaker = new DefaultCircuitBreaker(quickService, 3000, 2,
+ 2000 * 1000 * 1000);
+
+ //Create an object of monitoring service which makes both local and remote calls
+ var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,
+ quickServiceCircuitBreaker);
+
+ //Fetch response from local resource
+ LOGGER.info(monitoringService.localResourceResponse());
+
+ //Fetch response from delayed service 2 times, to meet the failure threshold
+ LOGGER.info(monitoringService.delayedServiceResponse());
+ LOGGER.info(monitoringService.delayedServiceResponse());
+
+ //Fetch current state of delayed service circuit breaker after crossing failure threshold limit
+ //which is OPEN now
+ LOGGER.info(delayedServiceCircuitBreaker.getState());
+
+ //Meanwhile, the delayed service is down, fetch response from the healthy quick service
+ LOGGER.info(monitoringService.quickServiceResponse());
+ LOGGER.info(quickServiceCircuitBreaker.getState());
+
+ //Wait for the delayed service to become responsive
+ try {
+ LOGGER.info("Waiting for delayed service to become responsive");
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
}
+ //Check the state of delayed circuit breaker, should be HALF_OPEN
+ LOGGER.info(delayedServiceCircuitBreaker.getState());
+
+ //Fetch response from delayed service, which should be healthy by now
+ LOGGER.info(monitoringService.delayedServiceResponse());
+ //As successful response is fetched, it should be CLOSED again.
+ LOGGER.info(delayedServiceCircuitBreaker.getState());
}
}
diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/CircuitBreaker.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/CircuitBreaker.java
index 18268b1ce..92884258b 100644
--- a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/CircuitBreaker.java
+++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/CircuitBreaker.java
@@ -24,114 +24,22 @@
package com.iluwatar.circuitbreaker;
/**
- * The circuit breaker class with all configurations.
+ * The Circuit breaker interface.
*/
-public class CircuitBreaker {
- private final long timeout;
- private final long retryTimePeriod;
- long lastFailureTime;
- int failureCount;
- private final int failureThreshold;
- private State state;
- private final long futureTime = 1000 * 1000 * 1000 * 1000;
+public interface CircuitBreaker {
- /**
- * Constructor to create an instance of Circuit Breaker.
- *
- * @param timeout Timeout for the API request. Not necessary for this simple example
- * @param failureThreshold Number of failures we receive from the depended service before changing
- * state to 'OPEN'
- * @param retryTimePeriod Time period after which a new request is made to remote service for
- * status check.
- */
- CircuitBreaker(long timeout, int failureThreshold, long retryTimePeriod) {
- // We start in a closed state hoping that everything is fine
- this.state = State.CLOSED;
- this.failureThreshold = failureThreshold;
- // Timeout for the API request.
- // Used to break the calls made to remote resource if it exceeds the limit
- this.timeout = timeout;
- this.retryTimePeriod = retryTimePeriod;
- //An absurd amount of time in future which basically indicates the last failure never happened
- this.lastFailureTime = System.nanoTime() + futureTime;
- this.failureCount = 0;
- }
+ // Success response. Reset everything to defaults
+ void recordSuccess();
- //Reset everything to defaults
- private void reset() {
- this.failureCount = 0;
- this.lastFailureTime = System.nanoTime() + futureTime;
- this.state = State.CLOSED;
- }
+ // Failure response. Handle accordingly with response and change state if required.
+ void recordFailure(String response);
- private void recordFailure() {
- failureCount = failureCount + 1;
- this.lastFailureTime = System.nanoTime();
- }
+ // Get the current state of circuit breaker
+ String getState();
- protected void setState() {
- if (failureCount > failureThreshold) { //Then something is wrong with remote service
- if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) {
- //We have waited long enough and should try checking if service is up
- state = State.HALF_OPEN;
- } else {
- //Service would still probably be down
- state = State.OPEN;
- }
- } else {
- //Everything is working fine
- state = State.CLOSED;
- }
- }
+ // Set the specific state manually.
+ void setState(State state);
- public String getState() {
- return state.name();
- }
-
- /**
- * Break the circuit beforehand if it is known service is down Or connect the circuit manually if
- * service comes online before expected.
- *
- * @param state State at which circuit is in
- */
- public void setStateForBypass(State state) {
- this.state = state;
- }
-
- /**
- * Executes service call.
- *
- * @param serviceToCall The name of the service in String. Can be changed to data URLs in case
- * of web applications
- * @param serverStartTime Time at which actual server was started which makes calls to this
- * service
- * @return Value from the remote resource, stale response or a custom exception
- */
- public String call(String serviceToCall, long serverStartTime) throws Exception {
- setState();
- if (state == State.OPEN) {
- // return cached response if no the circuit is in OPEN state
- return "This is stale response from API";
- } else {
- // Make the API request if the circuit is not OPEN
- if (serviceToCall.equals("delayedService")) {
- var delayedService = new DelayedService(20);
- var response = delayedService.response(serverStartTime);
- //In a real application, this would be run in a thread and the timeout
- //parameter of the circuit breaker would be utilized to know if service
- //is working. Here, we simulate that based on server response itself
- if (response.split(" ")[3].equals("working")) {
- // Yay!! the API responded fine. Let's reset everything.
- reset();
- return response;
- } else {
- // Uh-oh!! the call still failed. Let's update that in our records.
- recordFailure();
- throw new Exception("Remote service not responding");
- }
- } else {
- throw new Exception("Unknown Service Name");
- }
- }
- }
-}
\ No newline at end of file
+ // Attempt to fetch response from the remote service.
+ String attemptRequest() throws RemoteServiceException;
+}
diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DefaultCircuitBreaker.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DefaultCircuitBreaker.java
new file mode 100644
index 000000000..7e45f5709
--- /dev/null
+++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DefaultCircuitBreaker.java
@@ -0,0 +1,155 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.circuitbreaker;
+
+/**
+ * The delay based Circuit breaker implementation that works in a
+ * CLOSED->OPEN-(retry_time_period)->HALF_OPEN->CLOSED flow with some retry time period for failed
+ * services and a failure threshold for service to open circuit.
+ */
+public class DefaultCircuitBreaker implements CircuitBreaker {
+
+ private final long timeout;
+ private final long retryTimePeriod;
+ private final RemoteService service;
+ long lastFailureTime;
+ private String lastFailureResponse;
+ int failureCount;
+ private final int failureThreshold;
+ private State state;
+ private final long futureTime = 1000 * 1000 * 1000 * 1000;
+
+ /**
+ * Constructor to create an instance of Circuit Breaker.
+ *
+ * @param timeout Timeout for the API request. Not necessary for this simple example
+ * @param failureThreshold Number of failures we receive from the depended service before changing
+ * state to 'OPEN'
+ * @param retryTimePeriod Time period after which a new request is made to remote service for
+ * status check.
+ */
+ DefaultCircuitBreaker(RemoteService serviceToCall, long timeout, int failureThreshold,
+ long retryTimePeriod) {
+ this.service = serviceToCall;
+ // We start in a closed state hoping that everything is fine
+ this.state = State.CLOSED;
+ this.failureThreshold = failureThreshold;
+ // Timeout for the API request.
+ // Used to break the calls made to remote resource if it exceeds the limit
+ this.timeout = timeout;
+ this.retryTimePeriod = retryTimePeriod;
+ //An absurd amount of time in future which basically indicates the last failure never happened
+ this.lastFailureTime = System.nanoTime() + futureTime;
+ this.failureCount = 0;
+ }
+
+ // Reset everything to defaults
+ @Override
+ public void recordSuccess() {
+ this.failureCount = 0;
+ this.lastFailureTime = System.nanoTime() + futureTime;
+ this.state = State.CLOSED;
+ }
+
+ @Override
+ public void recordFailure(String response) {
+ failureCount = failureCount + 1;
+ this.lastFailureTime = System.nanoTime();
+ // Cache the failure response for returning on open state
+ this.lastFailureResponse = response;
+ }
+
+ // Evaluate the current state based on failureThreshold, failureCount and lastFailureTime.
+ protected void evaluateState() {
+ if (failureCount >= failureThreshold) { //Then something is wrong with remote service
+ if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) {
+ //We have waited long enough and should try checking if service is up
+ state = State.HALF_OPEN;
+ } else {
+ //Service would still probably be down
+ state = State.OPEN;
+ }
+ } else {
+ //Everything is working fine
+ state = State.CLOSED;
+ }
+ }
+
+ @Override
+ public String getState() {
+ evaluateState();
+ return state.name();
+ }
+
+ /**
+ * Break the circuit beforehand if it is known service is down Or connect the circuit manually if
+ * service comes online before expected.
+ *
+ * @param state State at which circuit is in
+ */
+ @Override
+ public void setState(State state) {
+ this.state = state;
+ switch (state) {
+ case OPEN:
+ this.failureCount = failureThreshold;
+ this.lastFailureTime = System.nanoTime();
+ break;
+ case HALF_OPEN:
+ this.failureCount = failureThreshold;
+ this.lastFailureTime = System.nanoTime() - retryTimePeriod;
+ break;
+ default:
+ this.failureCount = 0;
+ }
+ }
+
+ /**
+ * Executes service call.
+ *
+ * @return Value from the remote resource, stale response or a custom exception
+ */
+ @Override
+ public String attemptRequest() throws RemoteServiceException {
+ evaluateState();
+ if (state == State.OPEN) {
+ // return cached response if the circuit is in OPEN state
+ return this.lastFailureResponse;
+ } else {
+ // Make the API request if the circuit is not OPEN
+ try {
+ //In a real application, this would be run in a thread and the timeout
+ //parameter of the circuit breaker would be utilized to know if service
+ //is working. Here, we simulate that based on server response itself
+ var response = service.call();
+ // Yay!! the API responded fine. Let's reset everything.
+ recordSuccess();
+ return response;
+ } catch (RemoteServiceException ex) {
+ recordFailure(ex.getMessage());
+ throw ex;
+ }
+ }
+ }
+}
diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DelayedService.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DelayedRemoteService.java
similarity index 81%
rename from circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DelayedService.java
rename to circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DelayedRemoteService.java
index 13861923b..f4add64fc 100644
--- a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DelayedService.java
+++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DelayedRemoteService.java
@@ -27,7 +27,9 @@ package com.iluwatar.circuitbreaker;
* This simulates the remote service It responds only after a certain timeout period (default set to
* 20 seconds).
*/
-public class DelayedService {
+public class DelayedRemoteService implements RemoteService {
+
+ private final long serverStartTime;
private final int delay;
/**
@@ -35,22 +37,23 @@ public class DelayedService {
*
* @param delay the delay after which service would behave properly, in seconds
*/
- public DelayedService(int delay) {
+ public DelayedRemoteService(long serverStartTime, int delay) {
+ this.serverStartTime = serverStartTime;
this.delay = delay;
}
- public DelayedService() {
- this.delay = 60;
+ public DelayedRemoteService() {
+ this.serverStartTime = System.nanoTime();
+ this.delay = 20;
}
/**
* Responds based on delay, current time and server start time if the service is down / working.
*
- * @param serverStartTime Time at which actual server was started which makes calls to this
- * service
* @return The state of the service
*/
- public String response(long serverStartTime) {
+ @Override
+ public String call() throws RemoteServiceException {
var currentTime = System.nanoTime();
//Since currentTime and serverStartTime are both in nanoseconds, we convert it to
//seconds by diving by 10e9 and ensure floating point division by multiplying it
@@ -58,9 +61,8 @@ public class DelayedService {
//send the reply
if ((currentTime - serverStartTime) * 1.0 / (1000 * 1000 * 1000) < delay) {
//Can use Thread.sleep() here to block and simulate a hung server
- return "Delayed service is down";
- } else {
- return "Delayed service is working";
+ throw new RemoteServiceException("Delayed service is down");
}
+ return "Delayed service is working";
}
}
diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/MonitoringService.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/MonitoringService.java
index e91367175..3fb8d8396 100644
--- a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/MonitoringService.java
+++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/MonitoringService.java
@@ -24,28 +24,47 @@
package com.iluwatar.circuitbreaker;
/**
- * The service class which makes local and remote calls Uses {@link CircuitBreaker} object to ensure
- * remote calls don't use up resources.
+ * The service class which makes local and remote calls Uses {@link DefaultCircuitBreaker} object to
+ * ensure remote calls don't use up resources.
*/
public class MonitoringService {
+ private final CircuitBreaker delayedService;
+
+ private final CircuitBreaker quickService;
+
+ public MonitoringService(CircuitBreaker delayedService, CircuitBreaker quickService) {
+ this.delayedService = delayedService;
+ this.quickService = quickService;
+ }
+
//Assumption: Local service won't fail, no need to wrap it in a circuit breaker logic
public String localResourceResponse() {
return "Local Service is working";
}
/**
- * Try to get result from remote server.
+ * Fetch response from the delayed service (with some simulated startup time).
*
- * @param circuitBreaker The circuitBreaker object with all parameters
- * @param serverStartTime Time at which actual server was started which makes calls to this
- * service
- * @return result from the remote response or exception raised by it.
+ * @return response string
*/
- public String remoteResourceResponse(CircuitBreaker circuitBreaker, long serverStartTime) {
+ public String delayedServiceResponse() {
try {
- return circuitBreaker.call("delayedService", serverStartTime);
- } catch (Exception e) {
+ return this.delayedService.attemptRequest();
+ } catch (RemoteServiceException e) {
+ return e.getMessage();
+ }
+ }
+
+ /**
+ * Fetches response from a healthy service without any failure.
+ *
+ * @return response string
+ */
+ public String quickServiceResponse() {
+ try {
+ return this.quickService.attemptRequest();
+ } catch (RemoteServiceException e) {
return e.getMessage();
}
}
diff --git a/command/src/main/java/com/iluwatar/command/Command.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/QuickRemoteService.java
similarity index 80%
rename from command/src/main/java/com/iluwatar/command/Command.java
rename to circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/QuickRemoteService.java
index 85deff74e..1b73bc0e3 100644
--- a/command/src/main/java/com/iluwatar/command/Command.java
+++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/QuickRemoteService.java
@@ -1,40 +1,35 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.command;
-
-/**
- * Interface for Commands.
- */
-public abstract class Command {
-
- public abstract void execute(Target target);
-
- public abstract void undo();
-
- public abstract void redo();
-
- @Override
- public abstract String toString();
-
-}
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.circuitbreaker;
+
+/**
+ * A quick response remote service, that responds healthy without any delay or failure.
+ */
+public class QuickRemoteService implements RemoteService {
+
+ @Override
+ public String call() throws RemoteServiceException {
+ return "Quick Service is working";
+ }
+}
diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/RemoteService.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/RemoteService.java
new file mode 100644
index 000000000..3c2fd9c78
--- /dev/null
+++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/RemoteService.java
@@ -0,0 +1,34 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.circuitbreaker;
+
+/**
+ * The Remote service interface, used by {@link CircuitBreaker} for fetching response from remote
+ * services.
+ */
+public interface RemoteService {
+
+ //Fetch response from remote service.
+ String call() throws RemoteServiceException;
+}
diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/RemoteServiceException.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/RemoteServiceException.java
new file mode 100644
index 000000000..b34f0b3fe
--- /dev/null
+++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/RemoteServiceException.java
@@ -0,0 +1,34 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.circuitbreaker;
+
+/**
+ * Exception thrown when {@link RemoteService} does not respond successfully.
+ */
+public class RemoteServiceException extends Exception {
+
+ public RemoteServiceException(String message) {
+ super(message);
+ }
+}
diff --git a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/AppTest.java b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/AppTest.java
new file mode 100644
index 000000000..d28d316c8
--- /dev/null
+++ b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/AppTest.java
@@ -0,0 +1,135 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.circuitbreaker;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * App Test showing usage of circuit breaker.
+ */
+public class AppTest {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(AppTest.class);
+
+ //Startup delay for delayed service (in seconds)
+ private static final int STARTUP_DELAY = 4;
+
+ //Number of failed requests for circuit breaker to open
+ private static final int FAILURE_THRESHOLD = 1;
+
+ //Time period in seconds for circuit breaker to retry service
+ private static final int RETRY_PERIOD = 2;
+
+ private MonitoringService monitoringService;
+
+ private CircuitBreaker delayedServiceCircuitBreaker;
+
+ private CircuitBreaker quickServiceCircuitBreaker;
+
+ /**
+ * Setup the circuit breakers and services, where {@link DelayedRemoteService} will be start with
+ * a delay of 4 seconds and a {@link QuickRemoteService} responding healthy. Both services are
+ * wrapped in a {@link DefaultCircuitBreaker} implementation with failure threshold of 1 failure
+ * and retry time period of 2 seconds.
+ */
+ @BeforeEach
+ public void setupCircuitBreakers() {
+ var delayedService = new DelayedRemoteService(System.nanoTime(), STARTUP_DELAY);
+ //Set the circuit Breaker parameters
+ delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000,
+ FAILURE_THRESHOLD,
+ RETRY_PERIOD * 1000 * 1000 * 1000);
+
+ var quickService = new QuickRemoteService();
+ //Set the circuit Breaker parameters
+ quickServiceCircuitBreaker = new DefaultCircuitBreaker(quickService, 3000, FAILURE_THRESHOLD,
+ RETRY_PERIOD * 1000 * 1000 * 1000);
+
+ monitoringService = new MonitoringService(delayedServiceCircuitBreaker,
+ quickServiceCircuitBreaker);
+
+ }
+
+ @Test
+ public void testFailure_OpenStateTransition() {
+ //Calling delayed service, which will be unhealthy till 4 seconds
+ assertEquals("Delayed service is down", monitoringService.delayedServiceResponse());
+ //As failure threshold is "1", the circuit breaker is changed to OPEN
+ assertEquals("OPEN", delayedServiceCircuitBreaker.getState());
+ //As circuit state is OPEN, we expect a quick fallback response from circuit breaker.
+ assertEquals("Delayed service is down", monitoringService.delayedServiceResponse());
+
+ //Meanwhile, the quick service is responding and the circuit state is CLOSED
+ assertEquals("Quick Service is working", monitoringService.quickServiceResponse());
+ assertEquals("CLOSED", quickServiceCircuitBreaker.getState());
+
+ }
+
+ @Test
+ public void testFailure_HalfOpenStateTransition() {
+ //Calling delayed service, which will be unhealthy till 4 seconds
+ assertEquals("Delayed service is down", monitoringService.delayedServiceResponse());
+ //As failure threshold is "1", the circuit breaker is changed to OPEN
+ assertEquals("OPEN", delayedServiceCircuitBreaker.getState());
+
+ //Waiting for recovery period of 2 seconds for circuit breaker to retry service.
+ try {
+ LOGGER.info("Waiting 2s for delayed service to become responsive");
+ Thread.sleep(2000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ //After 2 seconds, the circuit breaker should move to "HALF_OPEN" state and retry fetching response from service again
+ assertEquals("HALF_OPEN", delayedServiceCircuitBreaker.getState());
+
+ }
+
+ @Test
+ public void testRecovery_ClosedStateTransition() {
+ //Calling delayed service, which will be unhealthy till 4 seconds
+ assertEquals("Delayed service is down", monitoringService.delayedServiceResponse());
+ //As failure threshold is "1", the circuit breaker is changed to OPEN
+ assertEquals("OPEN", delayedServiceCircuitBreaker.getState());
+
+ //Waiting for 4 seconds, which is enough for DelayedService to become healthy and respond successfully.
+ try {
+ LOGGER.info("Waiting 4s for delayed service to become responsive");
+ Thread.sleep(4000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ //As retry period is 2 seconds (<4 seconds of wait), hence the circuit breaker should be back in HALF_OPEN state.
+ assertEquals("HALF_OPEN", delayedServiceCircuitBreaker.getState());
+ //Check the success response from delayed service.
+ assertEquals("Delayed service is working", monitoringService.delayedServiceResponse());
+ //As the response is success, the state should be CLOSED
+ assertEquals("CLOSED", delayedServiceCircuitBreaker.getState());
+ }
+
+}
diff --git a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/CircuitBreakerTest.java b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DefaultCircuitBreakerTest.java
similarity index 70%
rename from circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/CircuitBreakerTest.java
rename to circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DefaultCircuitBreakerTest.java
index 98b59a6ae..4d300b36f 100644
--- a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/CircuitBreakerTest.java
+++ b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DefaultCircuitBreakerTest.java
@@ -25,56 +25,60 @@ package com.iluwatar.circuitbreaker;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import java.rmi.Remote;
import org.junit.jupiter.api.Test;
/**
* Circuit Breaker test
*/
-public class CircuitBreakerTest {
+public class DefaultCircuitBreakerTest {
//long timeout, int failureThreshold, long retryTimePeriod
@Test
- public void testSetState() {
- var circuitBreaker = new CircuitBreaker(1, 1, 100);
+ public void testEvaluateState() {
+ var circuitBreaker = new DefaultCircuitBreaker(null, 1, 1, 100);
//Right now, failureCountfailureThreshold, and lastFailureTime is nearly equal to current time,
//state should be half-open
assertEquals(circuitBreaker.getState(), "HALF_OPEN");
//Since failureCount>failureThreshold, and lastFailureTime is much lesser current time,
//state should be open
circuitBreaker.lastFailureTime = System.nanoTime() - 1000 * 1000 * 1000 * 1000;
- circuitBreaker.setState();
+ circuitBreaker.evaluateState();
assertEquals(circuitBreaker.getState(), "OPEN");
//Now set it back again to closed to test idempotency
circuitBreaker.failureCount = 0;
- circuitBreaker.setState();
+ circuitBreaker.evaluateState();
assertEquals(circuitBreaker.getState(), "CLOSED");
}
@Test
public void testSetStateForBypass() {
- var circuitBreaker = new CircuitBreaker(1, 1, 100);
+ var circuitBreaker = new DefaultCircuitBreaker(null, 1, 1, 2000 * 1000 * 1000);
//Right now, failureCount {
+ var obj = new DelayedRemoteService();
+ obj.call();
+ });
+ }
+
+ /**
+ * Testing server started in past (2 seconds ago) and with a simulated delay of 1 second.
+ *
+ * @throws RemoteServiceException
+ */
+ @Test
+ public void testParameterizedConstructor() throws RemoteServiceException {
+ var obj = new DelayedRemoteService(System.nanoTime()-2000*1000*1000,1);
+ assertEquals("Delayed service is working",obj.call());
+ }
+}
diff --git a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/MonitoringServiceTest.java b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/MonitoringServiceTest.java
index 71ede3e1c..f6b802b96 100644
--- a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/MonitoringServiceTest.java
+++ b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/MonitoringServiceTest.java
@@ -35,28 +35,45 @@ public class MonitoringServiceTest {
//long timeout, int failureThreshold, long retryTimePeriod
@Test
public void testLocalResponse() {
- var monitoringService = new MonitoringService();
+ var monitoringService = new MonitoringService(null,null);
var response = monitoringService.localResourceResponse();
assertEquals(response, "Local Service is working");
}
@Test
- public void testRemoteResponse() {
- var monitoringService = new MonitoringService();
- var circuitBreaker = new CircuitBreaker(1, 1, 100);
+ public void testDelayedRemoteResponseSuccess() {
+ var delayedService = new DelayedRemoteService(System.nanoTime()-2*1000*1000*1000, 2);
+ var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000,
+ 1,
+ 2 * 1000 * 1000 * 1000);
+
+ var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,null);
//Set time in past to make the server work
- var serverStartTime = System.nanoTime() / 10;
- var response = monitoringService.remoteResourceResponse(circuitBreaker, serverStartTime);
+ var response = monitoringService.delayedServiceResponse();
assertEquals(response, "Delayed service is working");
}
@Test
- public void testRemoteResponse2() {
- var monitoringService = new MonitoringService();
- var circuitBreaker = new CircuitBreaker(1, 1, 100);
+ public void testDelayedRemoteResponseFailure() {
+ var delayedService = new DelayedRemoteService(System.nanoTime(), 2);
+ var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000,
+ 1,
+ 2 * 1000 * 1000 * 1000);
+ var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,null);
//Set time as current time as initially server fails
- var serverStartTime = System.nanoTime();
- var response = monitoringService.remoteResourceResponse(circuitBreaker, serverStartTime);
- assertEquals(response, "Remote service not responding");
+ var response = monitoringService.delayedServiceResponse();
+ assertEquals(response, "Delayed service is down");
+ }
+
+ @Test
+ public void testQuickRemoteServiceResponse() {
+ var delayedService = new QuickRemoteService();
+ var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000,
+ 1,
+ 2 * 1000 * 1000 * 1000);
+ var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,null);
+ //Set time as current time as initially server fails
+ var response = monitoringService.delayedServiceResponse();
+ assertEquals(response, "Quick Service is working");
}
}
diff --git a/collection-pipeline/pom.xml b/collection-pipeline/pom.xml
index 6d8d467ad..08c41880b 100644
--- a/collection-pipeline/pom.xml
+++ b/collection-pipeline/pom.xml
@@ -27,7 +27,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
collection-pipeline
diff --git a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Car.java b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Car.java
index 2828cffd4..cffdc7c82 100644
--- a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Car.java
+++ b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Car.java
@@ -87,10 +87,7 @@ public class Car {
} else if (!model.equals(other.model)) {
return false;
}
- if (year != other.year) {
- return false;
- }
- return true;
+ return year == other.year;
}
public String getMake() {
diff --git a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Person.java b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Person.java
index 2e564b701..3e25f6993 100644
--- a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Person.java
+++ b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Person.java
@@ -29,7 +29,7 @@ import java.util.List;
* A Person class that has the list of cars that the person owns and use.
*/
public class Person {
- private List cars;
+ private final List cars;
/**
* Constructor to create an instance of person.
diff --git a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/module-info.java b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/module-info.java
deleted file mode 100644
index f8bd30a68..000000000
--- a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/module-info.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.
- */
-
-module com.iluwatar.collectionpipeline {
- requires org.slf4j;
-}
\ No newline at end of file
diff --git a/collection-pipeline/src/test/java/com/iluwatar/collectionpipeline/AppTest.java b/collection-pipeline/src/test/java/com/iluwatar/collectionpipeline/AppTest.java
index 6bf373e81..cedc492b9 100644
--- a/collection-pipeline/src/test/java/com/iluwatar/collectionpipeline/AppTest.java
+++ b/collection-pipeline/src/test/java/com/iluwatar/collectionpipeline/AppTest.java
@@ -37,7 +37,7 @@ import org.slf4j.LoggerFactory;
public class AppTest {
private static final Logger LOGGER = LoggerFactory.getLogger(AppTest.class);
- private List cars = CarFactory.createCars();
+ private final List cars = CarFactory.createCars();
@Test
public void testGetModelsAfter2000UsingFor() {
diff --git a/combinator/pom.xml b/combinator/pom.xml
index 3edfa7580..4886873cd 100644
--- a/combinator/pom.xml
+++ b/combinator/pom.xml
@@ -29,7 +29,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
combinator
@@ -39,6 +39,12 @@
junit
test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
diff --git a/combinator/src/test/java/com/iluwatar/combinator/CombinatorAppTest.java b/combinator/src/test/java/com/iluwatar/combinator/CombinatorAppTest.java
index f42b46c14..6e7b4f63f 100644
--- a/combinator/src/test/java/com/iluwatar/combinator/CombinatorAppTest.java
+++ b/combinator/src/test/java/com/iluwatar/combinator/CombinatorAppTest.java
@@ -25,12 +25,19 @@ package com.iluwatar.combinator;
import org.junit.Test;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
public class CombinatorAppTest {
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link CombinatorApp#main(String[])}
+ * throws an exception.
+ */
+
@Test
- public void main() {
- CombinatorApp.main(new String[]{});
+ public void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> CombinatorApp.main(new String[]{}));
}
}
\ No newline at end of file
diff --git a/command/README.md b/command/README.md
index 02a290e4d..08783679b 100644
--- a/command/README.md
+++ b/command/README.md
@@ -9,15 +9,20 @@ tags:
---
## Also known as
+
Action, Transaction
## Intent
-Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
+
+Encapsulate a request as an object, thereby letting you parameterize clients with different
+requests, queue or log requests, and support undoable operations.
## Explanation
Real world example
-> There is a wizard casting spells on a goblin. The spells are executed on the goblin one by one. The first spell shrinks the goblin and the second makes him invisible. Then the wizard reverses the spells one by one. Each spell here is a command object that can be undone.
+> There is a wizard casting spells on a goblin. The spells are executed on the goblin one by one.
+> The first spell shrinks the goblin and the second makes him invisible. Then the wizard reverses
+> the spells one by one. Each spell here is a command object that can be undone.
In plain words
@@ -25,34 +30,32 @@ In plain words
Wikipedia says
-> In object-oriented programming, the command pattern is a behavioral design pattern in which an object is used to encapsulate all information needed to perform an action or trigger an event at a later time.
+> In object-oriented programming, the command pattern is a behavioral design pattern in which an
+> object is used to encapsulate all information needed to perform an action or trigger an event at
+> a later time.
**Programmatic Example**
-Here's the sample code with wizard and goblin. Let's start from the wizard class.
+Here's the sample code with wizard and goblin. Let's start from the `Wizard` class.
```java
public class Wizard {
- private static final Logger LOGGER = LoggerFactory.getLogger(Wizard.class);
-
- private Deque undoStack = new LinkedList<>();
- private Deque redoStack = new LinkedList<>();
+ private final Deque undoStack = new LinkedList<>();
+ private final Deque redoStack = new LinkedList<>();
public Wizard() {}
- public void castSpell(Command command, Target target) {
- LOGGER.info("{} casts {} at {}", this, command, target);
- command.execute(target);
- undoStack.offerLast(command);
+ public void castSpell(Runnable runnable) {
+ runnable.run();
+ undoStack.offerLast(runnable);
}
public void undoLastSpell() {
if (!undoStack.isEmpty()) {
var previousSpell = undoStack.pollLast();
redoStack.offerLast(previousSpell);
- LOGGER.info("{} undoes {}", this, previousSpell);
- previousSpell.undo();
+ previousSpell.run();
}
}
@@ -60,8 +63,7 @@ public class Wizard {
if (!redoStack.isEmpty()) {
var previousSpell = redoStack.pollLast();
undoStack.offerLast(previousSpell);
- LOGGER.info("{} redoes {}", this, previousSpell);
- previousSpell.redo();
+ previousSpell.run();
}
}
@@ -72,85 +74,7 @@ public class Wizard {
}
```
-Next we present the spell hierarchy.
-
-```java
-public abstract class Command {
-
- public abstract void execute(Target target);
-
- public abstract void undo();
-
- public abstract void redo();
-
- @Override
- public abstract String toString();
-}
-
-public class InvisibilitySpell extends Command {
-
- private Target target;
-
- @Override
- public void execute(Target target) {
- target.setVisibility(Visibility.INVISIBLE);
- this.target = target;
- }
-
- @Override
- public void undo() {
- if (target != null) {
- target.setVisibility(Visibility.VISIBLE);
- }
- }
-
- @Override
- public void redo() {
- if (target != null) {
- target.setVisibility(Visibility.INVISIBLE);
- }
- }
-
- @Override
- public String toString() {
- return "Invisibility spell";
- }
-}
-
-public class ShrinkSpell extends Command {
-
- private Size oldSize;
- private Target target;
-
- @Override
- public void execute(Target target) {
- oldSize = target.getSize();
- target.setSize(Size.SMALL);
- this.target = target;
- }
-
- @Override
- public void undo() {
- if (oldSize != null && target != null) {
- var temp = target.getSize();
- target.setSize(oldSize);
- oldSize = temp;
- }
- }
-
- @Override
- public void redo() {
- undo();
- }
-
- @Override
- public String toString() {
- return "Shrink spell";
- }
-}
-```
-
-And last we have the goblin who's the target of the spells.
+Next, we have the goblin who's the target of the spells.
```java
public abstract class Target {
@@ -197,47 +121,110 @@ public class Goblin extends Target {
return "Goblin";
}
+ public void changeSize() {
+ var oldSize = getSize() == Size.NORMAL ? Size.SMALL : Size.NORMAL;
+ setSize(oldSize);
+ }
+
+ public void changeVisibility() {
+ var visible = getVisibility() == Visibility.INVISIBLE
+ ? Visibility.VISIBLE : Visibility.INVISIBLE;
+ setVisibility(visible);
+ }
}
```
-Finally here's the whole example in action.
+Finally we have the wizard in main function who casts spell
+
+```java
+public static void main(String[] args) {
+ var wizard = new Wizard();
+ var goblin = new Goblin();
+
+ // casts shrink/unshrink spell
+ wizard.castSpell(goblin::changeSize);
+
+ // casts visible/invisible spell
+ wizard.castSpell(goblin::changeVisibility);
+
+ // undo and redo casts
+ wizard.undoLastSpell();
+ wizard.redoLastSpell();
+```
+
+Here's the whole example in action.
```java
var wizard = new Wizard();
var goblin = new Goblin();
+
goblin.printStatus();
-// Goblin, [size=normal] [visibility=visible]
-wizard.castSpell(new ShrinkSpell(), goblin);
-// Wizard casts Shrink spell at Goblin
+wizard.castSpell(goblin::changeSize);
goblin.printStatus();
-// Goblin, [size=small] [visibility=visible]
-wizard.castSpell(new InvisibilitySpell(), goblin);
-// Wizard casts Invisibility spell at Goblin
+
+wizard.castSpell(goblin::changeVisibility);
goblin.printStatus();
-// Goblin, [size=small] [visibility=invisible]
+
wizard.undoLastSpell();
-// Wizard undoes Invisibility spell
goblin.printStatus();
-// Goblin, [size=small] [visibility=visible]
+
+wizard.undoLastSpell();
+goblin.printStatus();
+
+wizard.redoLastSpell();
+goblin.printStatus();
+
+wizard.redoLastSpell();
+goblin.printStatus();
+```
+
+Here's the program output:
+
+```java
+Goblin, [size=normal] [visibility=visible]
+Goblin, [size=small] [visibility=visible]
+Goblin, [size=small] [visibility=invisible]
+Goblin, [size=small] [visibility=visible]
+Goblin, [size=normal] [visibility=visible]
+Goblin, [size=small] [visibility=visible]
+Goblin, [size=small] [visibility=invisible]
```
## Class diagram
+

## Applicability
-Use the Command pattern when you want to
-* parameterize objects by an action to perform. You can express such parameterization in a procedural language with a callback function, that is, a function that's registered somewhere to be called at a later point. Commands are an object-oriented replacement for callbacks.
-* specify, queue, and execute requests at different times. A Command object can have a lifetime independent of the original request. If the receiver of a request can be represented in an address space-independent way, then you can transfer a command object for the request to a different process and fulfill the request there
-* support undo. The Command's execute operation can store state for reversing its effects in the command itself. The Command interface must have an added Unexecute operation that reverses the effects of a previous call to execute. Executed commands are stored in a history list. Unlimited-level undo and redo is achieved by traversing this list backwards and forwards calling unexecute and execute, respectively
-* support logging changes so that they can be reapplied in case of a system crash. By augmenting the Command interface with load and store operations, you can keep a persistent log of changes. Recovering from a crash involves reloading logged commands from disk and re-executing them with the execute operation
-* structure a system around high-level operations build on primitive operations. Such a structure is common in information systems that support transactions. A transaction encapsulates a set of changes to data. The Command pattern offers a way to model transactions. Commands have a common interface, letting you invoke all transactions the same way. The pattern also makes it easy to extend the system with new transactions
+Use the Command pattern when you want to:
+
+* Parameterize objects by an action to perform. You can express such parameterization in a
+procedural language with a callback function, that is, a function that's registered somewhere to be
+called at a later point. Commands are an object-oriented replacement for callbacks.
+* Specify, queue, and execute requests at different times. A Command object can have a lifetime
+independent of the original request. If the receiver of a request can be represented in an address
+space-independent way, then you can transfer a command object for the request to a different process
+and fulfill the request there.
+* Support undo. The Command's execute operation can store state for reversing its effects in the
+command itself. The Command interface must have an added un-execute operation that reverses the
+effects of a previous call to execute. The executed commands are stored in a history list.
+Unlimited-level undo and redo is achieved by traversing this list backwards and forwards calling
+un-execute and execute, respectively.
+* Support logging changes so that they can be reapplied in case of a system crash. By augmenting the
+Command interface with load and store operations, you can keep a persistent log of changes.
+Recovering from a crash involves reloading logged commands from disk and re-executing them with
+the execute operation.
+* Structure a system around high-level operations build on primitive operations. Such a structure is
+common in information systems that support transactions. A transaction encapsulates a set of changes
+to data. The Command pattern offers a way to model transactions. Commands have a common interface,
+letting you invoke all transactions the same way. The pattern also makes it easy to extend the
+system with new transactions.
## Typical Use Case
-* to keep a history of requests
-* implement callback functionality
-* implement the undo functionality
+* To keep a history of requests
+* Implement callback functionality
+* Implement the undo functionality
## Real world examples
diff --git a/command/etc/command.png b/command/etc/command.png
index 81b47d6d0..0f026464e 100644
Binary files a/command/etc/command.png and b/command/etc/command.png differ
diff --git a/command/etc/command.ucls b/command/etc/command.ucls
index f0e0857d2..afc7e1762 100644
--- a/command/etc/command.ucls
+++ b/command/etc/command.ucls
@@ -1,116 +1,89 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/command/etc/command.urm.puml b/command/etc/command.urm.puml
index a8b773418..f85949c56 100644
--- a/command/etc/command.urm.puml
+++ b/command/etc/command.urm.puml
@@ -4,33 +4,11 @@ package com.iluwatar.command {
+ App()
+ main(args : String[]) {static}
}
- abstract class Command {
- + Command()
- + execute(Target) {abstract}
- + redo() {abstract}
- + toString() : String {abstract}
- + undo() {abstract}
- }
class Goblin {
+ Goblin()
+ toString() : String
- }
- class InvisibilitySpell {
- - target : Target
- + InvisibilitySpell()
- + execute(target : Target)
- + redo()
- + toString() : String
- + undo()
- }
- class ShrinkSpell {
- - oldSize : Size
- - target : Target
- + ShrinkSpell()
- + execute(target : Target)
- + redo()
- + toString() : String
- + undo()
+ + changeSize()
+ + changeVisibility()
}
enum Size {
+ NORMAL {static}
@@ -62,22 +40,19 @@ package com.iluwatar.command {
}
class Wizard {
- LOGGER : Logger {static}
- - redoStack : Deque
- - undoStack : Deque
+ - redoStack : Deque
+ - undoStack : Deque
+ Wizard()
- + castSpell(command : Command, target : Target)
+ + castSpell(Runnable : runnable)
+ redoLastSpell()
+ toString() : String
+ undoLastSpell()
}
}
Target --> "-size" Size
-Wizard --> "-undoStack" Command
-ShrinkSpell --> "-oldSize" Size
-InvisibilitySpell --> "-target" Target
-ShrinkSpell --> "-target" Target
+Wizard --> "-changeSize" Goblin
+Wizard --> "-changeVisibility" Goblin
Target --> "-visibility" Visibility
-Goblin --|> Target
-InvisibilitySpell --|> Command
-ShrinkSpell --|> Command
-@enduml
\ No newline at end of file
+Goblin --|> Target
+App --> "castSpell" Wizard
+@enduml
diff --git a/command/pom.xml b/command/pom.xml
index 50a14c45f..3869d4b44 100644
--- a/command/pom.xml
+++ b/command/pom.xml
@@ -29,7 +29,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
command
diff --git a/command/src/main/java/com/iluwatar/command/App.java b/command/src/main/java/com/iluwatar/command/App.java
index b4e54fd97..77396d37a 100644
--- a/command/src/main/java/com/iluwatar/command/App.java
+++ b/command/src/main/java/com/iluwatar/command/App.java
@@ -30,12 +30,10 @@ package com.iluwatar.command;
*
* Four terms always associated with the command pattern are command, receiver, invoker and
* client. A command object (spell) knows about the receiver (target) and invokes a method of the
- * receiver. Values for parameters of the receiver method are stored in the command. The receiver
- * then does the work. An invoker object (wizard) knows how to execute a command, and optionally
- * does bookkeeping about the command execution. The invoker does not know anything about a concrete
- * command, it knows only about command interface. Both an invoker object and several command
- * objects are held by a client object (app). The client decides which commands to execute at which
- * points. To execute a command, it passes the command object to the invoker object.
+ * receiver. An invoker object (wizard) receives a reference to the command to be executed and
+ * optionally does bookkeeping about the command execution. The invoker does not know anything
+ * about how the command is executed. The client decides which commands to execute at which
+ * points. To execute a command, it passes a reference of the function to the invoker object.
*
*
In other words, in this example the wizard casts spells on the goblin. The wizard keeps track
* of the previous spells cast, so it is easy to undo them. In addition, the wizard keeps track of
@@ -54,10 +52,10 @@ public class App {
goblin.printStatus();
- wizard.castSpell(new ShrinkSpell(), goblin);
+ wizard.castSpell(goblin::changeSize);
goblin.printStatus();
- wizard.castSpell(new InvisibilitySpell(), goblin);
+ wizard.castSpell(goblin::changeVisibility);
goblin.printStatus();
wizard.undoLastSpell();
diff --git a/command/src/main/java/com/iluwatar/command/Goblin.java b/command/src/main/java/com/iluwatar/command/Goblin.java
index 72ddc43b5..e430b65b5 100644
--- a/command/src/main/java/com/iluwatar/command/Goblin.java
+++ b/command/src/main/java/com/iluwatar/command/Goblin.java
@@ -37,5 +37,4 @@ public class Goblin extends Target {
public String toString() {
return "Goblin";
}
-
}
diff --git a/command/src/main/java/com/iluwatar/command/Size.java b/command/src/main/java/com/iluwatar/command/Size.java
index ae327d8b1..c9aeb7017 100644
--- a/command/src/main/java/com/iluwatar/command/Size.java
+++ b/command/src/main/java/com/iluwatar/command/Size.java
@@ -1,43 +1,43 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.command;
-
-/**
- * Enumeration for target size.
- */
-public enum Size {
-
- SMALL("small"), NORMAL("normal");
-
- private String title;
-
- Size(String title) {
- this.title = title;
- }
-
- @Override
- public String toString() {
- return title;
- }
-}
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.command;
+
+/**
+ * Enumeration for target size.
+ */
+public enum Size {
+
+ SMALL("small"), NORMAL("normal");
+
+ private final String title;
+
+ Size(String title) {
+ this.title = title;
+ }
+
+ @Override
+ public String toString() {
+ return title;
+ }
+}
diff --git a/command/src/main/java/com/iluwatar/command/Target.java b/command/src/main/java/com/iluwatar/command/Target.java
index f5ac4344c..419ad6f54 100644
--- a/command/src/main/java/com/iluwatar/command/Target.java
+++ b/command/src/main/java/com/iluwatar/command/Target.java
@@ -62,4 +62,21 @@ public abstract class Target {
public void printStatus() {
LOGGER.info("{}, [size={}] [visibility={}]", this, getSize(), getVisibility());
}
+
+ /**
+ * Changes the size of the target.
+ */
+ public void changeSize() {
+ var oldSize = getSize() == Size.NORMAL ? Size.SMALL : Size.NORMAL;
+ setSize(oldSize);
+ }
+
+ /**
+ * Changes the visibility of the target.
+ */
+ public void changeVisibility() {
+ var visible = getVisibility() == Visibility.INVISIBLE
+ ? Visibility.VISIBLE : Visibility.INVISIBLE;
+ setVisibility(visible);
+ }
}
diff --git a/command/src/main/java/com/iluwatar/command/Visibility.java b/command/src/main/java/com/iluwatar/command/Visibility.java
index 3c48990a0..8fe0ce7bb 100644
--- a/command/src/main/java/com/iluwatar/command/Visibility.java
+++ b/command/src/main/java/com/iluwatar/command/Visibility.java
@@ -1,43 +1,43 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.command;
-
-/**
- * Enumeration for target visibility.
- */
-public enum Visibility {
-
- VISIBLE("visible"), INVISIBLE("invisible");
-
- private String title;
-
- Visibility(String title) {
- this.title = title;
- }
-
- @Override
- public String toString() {
- return title;
- }
-}
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.command;
+
+/**
+ * Enumeration for target visibility.
+ */
+public enum Visibility {
+
+ VISIBLE("visible"), INVISIBLE("invisible");
+
+ private final String title;
+
+ Visibility(String title) {
+ this.title = title;
+ }
+
+ @Override
+ public String toString() {
+ return title;
+ }
+}
diff --git a/command/src/main/java/com/iluwatar/command/Wizard.java b/command/src/main/java/com/iluwatar/command/Wizard.java
index e0b973265..f63831c07 100644
--- a/command/src/main/java/com/iluwatar/command/Wizard.java
+++ b/command/src/main/java/com/iluwatar/command/Wizard.java
@@ -25,30 +25,24 @@ package com.iluwatar.command;
import java.util.Deque;
import java.util.LinkedList;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* Wizard is the invoker of the commands.
*/
public class Wizard {
- private static final Logger LOGGER = LoggerFactory.getLogger(Wizard.class);
-
- private Deque undoStack = new LinkedList<>();
- private Deque redoStack = new LinkedList<>();
+ private final Deque undoStack = new LinkedList<>();
+ private final Deque redoStack = new LinkedList<>();
public Wizard() {
- // comment to ignore sonar issue: LEVEL critical
}
/**
* Cast spell.
*/
- public void castSpell(Command command, Target target) {
- LOGGER.info("{} casts {} at {}", this, command, target);
- command.execute(target);
- undoStack.offerLast(command);
+ public void castSpell(Runnable runnable) {
+ runnable.run();
+ undoStack.offerLast(runnable);
}
/**
@@ -58,8 +52,7 @@ public class Wizard {
if (!undoStack.isEmpty()) {
var previousSpell = undoStack.pollLast();
redoStack.offerLast(previousSpell);
- LOGGER.info("{} undoes {}", this, previousSpell);
- previousSpell.undo();
+ previousSpell.run();
}
}
@@ -70,8 +63,7 @@ public class Wizard {
if (!redoStack.isEmpty()) {
var previousSpell = redoStack.pollLast();
undoStack.offerLast(previousSpell);
- LOGGER.info("{} redoes {}", this, previousSpell);
- previousSpell.redo();
+ previousSpell.run();
}
}
diff --git a/command/src/main/java/com/iluwatar/command/module-info.java b/command/src/main/java/com/iluwatar/command/module-info.java
deleted file mode 100644
index 0e0c0b31f..000000000
--- a/command/src/main/java/com/iluwatar/command/module-info.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.
- */
-
-module com.iluwatar.command {
- requires org.slf4j;
-}
\ No newline at end of file
diff --git a/command/src/test/java/com/iluwatar/command/AppTest.java b/command/src/test/java/com/iluwatar/command/AppTest.java
index cf691aba3..73d098fa3 100644
--- a/command/src/test/java/com/iluwatar/command/AppTest.java
+++ b/command/src/test/java/com/iluwatar/command/AppTest.java
@@ -25,12 +25,21 @@ package com.iluwatar.command;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Tests that Command example runs without errors.
*/
-public class AppTest {
+class AppTest {
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
+ * throws an exception.
+ */
@Test
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/command/src/test/java/com/iluwatar/command/CommandTest.java b/command/src/test/java/com/iluwatar/command/CommandTest.java
index 81f556010..76b8c9e58 100644
--- a/command/src/test/java/com/iluwatar/command/CommandTest.java
+++ b/command/src/test/java/com/iluwatar/command/CommandTest.java
@@ -56,10 +56,10 @@ public class CommandTest {
var wizard = new Wizard();
var goblin = new Goblin();
- wizard.castSpell(new ShrinkSpell(), goblin);
+ wizard.castSpell(goblin::changeSize);
verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.VISIBLE);
- wizard.castSpell(new InvisibilitySpell(), goblin);
+ wizard.castSpell(goblin::changeVisibility);
verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.INVISIBLE);
wizard.undoLastSpell();
diff --git a/commander/pom.xml b/commander/pom.xml
index 7ab29e421..baabc04fc 100644
--- a/commander/pom.xml
+++ b/commander/pom.xml
@@ -27,7 +27,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
commander
diff --git a/commander/src/main/java/com/iluwatar/commander/Commander.java b/commander/src/main/java/com/iluwatar/commander/Commander.java
index 41779c076..2909f9304 100644
--- a/commander/src/main/java/com/iluwatar/commander/Commander.java
+++ b/commander/src/main/java/com/iluwatar/commander/Commander.java
@@ -36,9 +36,11 @@ import com.iluwatar.commander.queue.QueueDatabase;
import com.iluwatar.commander.queue.QueueTask;
import com.iluwatar.commander.queue.QueueTask.TaskType;
import com.iluwatar.commander.shippingservice.ShippingService;
+import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+
/**
* Commander pattern is used to handle all issues that can come up while making a
* distributed transaction. The idea is to have a commander, which coordinates the execution of all
@@ -159,8 +161,8 @@ public class Commander {
private void sendPaymentRequest(Order order) {
if (System.currentTimeMillis() - order.createdTime >= this.paymentTime) {
- if (order.paid.equals(PaymentStatus.Trying)) {
- order.paid = PaymentStatus.NotDone;
+ if (order.paid.equals(PaymentStatus.TRYING)) {
+ order.paid = PaymentStatus.NOT_DONE;
sendPaymentFailureMessage(order);
LOG.error("Order " + order.id + ": Payment time for order over, failed and returning..");
} //if succeeded or failed, would have been dequeued, no attempt to make payment
@@ -172,15 +174,15 @@ public class Commander {
if (!l.isEmpty()) {
if (DatabaseUnavailableException.class.isAssignableFrom(l.get(0).getClass())) {
LOG.debug("Order " + order.id + ": Error in connecting to payment service,"
- + " trying again..");
+ + " trying again..");
} else {
LOG.debug("Order " + order.id + ": Error in creating payment request..");
}
throw l.remove(0);
}
- if (order.paid.equals(PaymentStatus.Trying)) {
+ if (order.paid.equals(PaymentStatus.TRYING)) {
var transactionId = paymentService.receiveRequest(order.price);
- order.paid = PaymentStatus.Done;
+ order.paid = PaymentStatus.DONE;
LOG.info("Order " + order.id + ": Payment successful, transaction Id: " + transactionId);
if (!finalSiteMsgShown) {
LOG.info("Payment made successfully, thank you for shopping with us!!");
@@ -193,26 +195,26 @@ public class Commander {
if (PaymentDetailsErrorException.class.isAssignableFrom(err.getClass())) {
if (!finalSiteMsgShown) {
LOG.info("There was an error in payment. Your account/card details "
- + "may have been incorrect. "
- + "Meanwhile, your order has been converted to COD and will be shipped.");
+ + "may have been incorrect. "
+ + "Meanwhile, your order has been converted to COD and will be shipped.");
finalSiteMsgShown = true;
}
LOG.error("Order " + order.id + ": Payment details incorrect, failed..");
- o.paid = PaymentStatus.NotDone;
+ o.paid = PaymentStatus.NOT_DONE;
sendPaymentFailureMessage(o);
} else {
- if (o.messageSent.equals(MessageSent.NoneSent)) {
+ if (o.messageSent.equals(MessageSent.NONE_SENT)) {
if (!finalSiteMsgShown) {
LOG.info("There was an error in payment. We are on it, and will get back to you "
- + "asap. Don't worry, your order has been placed and will be shipped.");
+ + "asap. Don't worry, your order has been placed and will be shipped.");
finalSiteMsgShown = true;
}
LOG.warn("Order " + order.id + ": Payment error, going to queue..");
sendPaymentPossibleErrorMsg(o);
}
- if (o.paid.equals(PaymentStatus.Trying) && System
- .currentTimeMillis() - o.createdTime < paymentTime) {
- var qt = new QueueTask(o, TaskType.Payment, -1);
+ if (o.paid.equals(PaymentStatus.TRYING) && System
+ .currentTimeMillis() - o.createdTime < paymentTime) {
+ var qt = new QueueTask(o, TaskType.PAYMENT, -1);
updateQueue(qt);
}
}
@@ -234,12 +236,12 @@ public class Commander {
// additional check not needed
LOG.trace("Order " + qt.order.id + ": Queue time for order over, failed..");
return;
- } else if (qt.taskType.equals(TaskType.Payment) && !qt.order.paid.equals(PaymentStatus.Trying)
- || qt.taskType.equals(TaskType.Messaging) && (qt.messageType == 1
- && !qt.order.messageSent.equals(MessageSent.NoneSent)
- || qt.order.messageSent.equals(MessageSent.PaymentFail)
- || qt.order.messageSent.equals(MessageSent.PaymentSuccessful))
- || qt.taskType.equals(TaskType.EmployeeDb) && qt.order.addedToEmployeeHandle) {
+ } else if (qt.taskType.equals(TaskType.PAYMENT) && !qt.order.paid.equals(PaymentStatus.TRYING)
+ || qt.taskType.equals(TaskType.MESSAGING) && (qt.messageType == 1
+ && !qt.order.messageSent.equals(MessageSent.NONE_SENT)
+ || qt.order.messageSent.equals(MessageSent.PAYMENT_FAIL)
+ || qt.order.messageSent.equals(MessageSent.PAYMENT_SUCCESSFUL))
+ || qt.taskType.equals(TaskType.EMPLOYEE_DB) && qt.order.addedToEmployeeHandle) {
LOG.trace("Order " + qt.order.id + ": Not queueing task since task already done..");
return;
}
@@ -256,8 +258,8 @@ public class Commander {
tryDoingTasksInQueue();
};
Retry.HandleErrorIssue handleError = (qt1, err) -> {
- if (qt1.taskType.equals(TaskType.Payment)) {
- qt1.order.paid = PaymentStatus.NotDone;
+ if (qt1.taskType.equals(TaskType.PAYMENT)) {
+ qt1.order.paid = PaymentStatus.NOT_DONE;
sendPaymentFailureMessage(qt1.order);
LOG.error("Order " + qt1.order.id + ": Unable to enqueue payment task,"
+ " payment failed..");
@@ -331,35 +333,9 @@ public class Commander {
}
var list = messagingService.exceptionsList;
Thread t = new Thread(() -> {
- Retry.Operation op = (l) -> {
- if (!l.isEmpty()) {
- if (DatabaseUnavailableException.class.isAssignableFrom(l.get(0).getClass())) {
- LOG.debug("Order " + order.id + ": Error in connecting to messaging service "
- + "(Payment Success msg), trying again..");
- } else {
- LOG.debug("Order " + order.id + ": Error in creating Payment Success"
- + " messaging request..");
- }
- throw l.remove(0);
- }
- if (!order.messageSent.equals(MessageSent.PaymentFail)
- && !order.messageSent.equals(MessageSent.PaymentSuccessful)) {
- var requestId = messagingService.receiveRequest(2);
- order.messageSent = MessageSent.PaymentSuccessful;
- LOG.info("Order " + order.id + ": Payment Success message sent,"
- + " request Id: " + requestId);
- }
- };
+ Retry.Operation op = handleSuccessMessageRetryOperation(order);
Retry.HandleErrorIssue handleError = (o, err) -> {
- if ((o.messageSent.equals(MessageSent.NoneSent) || o.messageSent
- .equals(MessageSent.PaymentTrying))
- && System.currentTimeMillis() - o.createdTime < messageTime) {
- var qt = new QueueTask(order, TaskType.Messaging, 2);
- updateQueue(qt);
- LOG.info("Order " + order.id + ": Error in sending Payment Success message, trying to"
- + " queue task and add to employee handle..");
- employeeHandleIssue(order);
- }
+ handleSuccessMessageErrorIssue(order, o);
};
var r = new Retry<>(op, handleError, numOfRetries, retryDuration,
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
@@ -372,6 +348,40 @@ public class Commander {
t.start();
}
+ private void handleSuccessMessageErrorIssue(Order order, Order o) {
+ if ((o.messageSent.equals(MessageSent.NONE_SENT) || o.messageSent
+ .equals(MessageSent.PAYMENT_TRYING))
+ && System.currentTimeMillis() - o.createdTime < messageTime) {
+ var qt = new QueueTask(order, TaskType.MESSAGING, 2);
+ updateQueue(qt);
+ LOG.info("Order " + order.id + ": Error in sending Payment Success message, trying to"
+ + " queue task and add to employee handle..");
+ employeeHandleIssue(order);
+ }
+ }
+
+ private Retry.Operation handleSuccessMessageRetryOperation(Order order) {
+ return (l) -> {
+ if (!l.isEmpty()) {
+ if (DatabaseUnavailableException.class.isAssignableFrom(l.get(0).getClass())) {
+ LOG.debug("Order " + order.id + ": Error in connecting to messaging service "
+ + "(Payment Success msg), trying again..");
+ } else {
+ LOG.debug("Order " + order.id + ": Error in creating Payment Success"
+ + " messaging request..");
+ }
+ throw l.remove(0);
+ }
+ if (!order.messageSent.equals(MessageSent.PAYMENT_FAIL)
+ && !order.messageSent.equals(MessageSent.PAYMENT_SUCCESSFUL)) {
+ var requestId = messagingService.receiveRequest(2);
+ order.messageSent = MessageSent.PAYMENT_SUCCESSFUL;
+ LOG.info("Order " + order.id + ": Payment Success message sent,"
+ + " request Id: " + requestId);
+ }
+ };
+ }
+
private void sendPaymentFailureMessage(Order order) {
if (System.currentTimeMillis() - order.createdTime >= this.messageTime) {
LOG.trace("Order " + order.id + ": Message time for order over, returning..");
@@ -380,34 +390,10 @@ public class Commander {
var list = messagingService.exceptionsList;
var t = new Thread(() -> {
Retry.Operation op = (l) -> {
- if (!l.isEmpty()) {
- if (DatabaseUnavailableException.class.isAssignableFrom(l.get(0).getClass())) {
- LOG.debug("Order " + order.id + ": Error in connecting to messaging service "
- + "(Payment Failure msg), trying again..");
- } else {
- LOG.debug("Order " + order.id + ": Error in creating Payment Failure"
- + " message request..");
- }
- throw l.remove(0);
- }
- if (!order.messageSent.equals(MessageSent.PaymentFail)
- && !order.messageSent.equals(MessageSent.PaymentSuccessful)) {
- var requestId = messagingService.receiveRequest(0);
- order.messageSent = MessageSent.PaymentFail;
- LOG.info("Order " + order.id + ": Payment Failure message sent successfully,"
- + " request Id: " + requestId);
- }
+ handlePaymentFailureRetryOperation(order, l);
};
Retry.HandleErrorIssue handleError = (o, err) -> {
- if ((o.messageSent.equals(MessageSent.NoneSent) || o.messageSent
- .equals(MessageSent.PaymentTrying))
- && System.currentTimeMillis() - o.createdTime < messageTime) {
- var qt = new QueueTask(order, TaskType.Messaging, 0);
- updateQueue(qt);
- LOG.warn("Order " + order.id + ": Error in sending Payment Failure message, "
- + "trying to queue task and add to employee handle..");
- employeeHandleIssue(o);
- }
+ handlePaymentErrorIssue(order, o);
};
var r = new Retry<>(op, handleError, numOfRetries, retryDuration,
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
@@ -420,6 +406,38 @@ public class Commander {
t.start();
}
+ private void handlePaymentErrorIssue(Order order, Order o) {
+ if ((o.messageSent.equals(MessageSent.NONE_SENT) || o.messageSent
+ .equals(MessageSent.PAYMENT_TRYING))
+ && System.currentTimeMillis() - o.createdTime < messageTime) {
+ var qt = new QueueTask(order, TaskType.MESSAGING, 0);
+ updateQueue(qt);
+ LOG.warn("Order " + order.id + ": Error in sending Payment Failure message, "
+ + "trying to queue task and add to employee handle..");
+ employeeHandleIssue(o);
+ }
+ }
+
+ private void handlePaymentFailureRetryOperation(Order order, List l) throws Exception {
+ if (!l.isEmpty()) {
+ if (DatabaseUnavailableException.class.isAssignableFrom(l.get(0).getClass())) {
+ LOG.debug("Order " + order.id + ": Error in connecting to messaging service "
+ + "(Payment Failure msg), trying again..");
+ } else {
+ LOG.debug("Order " + order.id + ": Error in creating Payment Failure"
+ + " message request..");
+ }
+ throw l.remove(0);
+ }
+ if (!order.messageSent.equals(MessageSent.PAYMENT_FAIL)
+ && !order.messageSent.equals(MessageSent.PAYMENT_SUCCESSFUL)) {
+ var requestId = messagingService.receiveRequest(0);
+ order.messageSent = MessageSent.PAYMENT_FAIL;
+ LOG.info("Order " + order.id + ": Payment Failure message sent successfully,"
+ + " request Id: " + requestId);
+ }
+ }
+
private void sendPaymentPossibleErrorMsg(Order order) {
if (System.currentTimeMillis() - order.createdTime >= this.messageTime) {
LOG.trace("Message time for order over, returning..");
@@ -428,34 +446,10 @@ public class Commander {
var list = messagingService.exceptionsList;
var t = new Thread(() -> {
Retry.Operation op = (l) -> {
- if (!l.isEmpty()) {
- if (DatabaseUnavailableException.class.isAssignableFrom(l.get(0).getClass())) {
- LOG.debug("Order " + order.id + ": Error in connecting to messaging service "
- + "(Payment Error msg), trying again..");
- } else {
- LOG.debug("Order " + order.id + ": Error in creating Payment Error"
- + " messaging request..");
- }
- throw l.remove(0);
- }
- if (order.paid.equals(PaymentStatus.Trying) && order.messageSent
- .equals(MessageSent.NoneSent)) {
- var requestId = messagingService.receiveRequest(1);
- order.messageSent = MessageSent.PaymentTrying;
- LOG.info("Order " + order.id + ": Payment Error message sent successfully,"
- + " request Id: " + requestId);
- }
+ handlePaymentPossibleErrorMsgRetryOperation(order, l);
};
Retry.HandleErrorIssue handleError = (o, err) -> {
- if (o.messageSent.equals(MessageSent.NoneSent) && order.paid
- .equals(PaymentStatus.Trying)
- && System.currentTimeMillis() - o.createdTime < messageTime) {
- var qt = new QueueTask(order, TaskType.Messaging, 1);
- updateQueue(qt);
- LOG.warn("Order " + order.id + ": Error in sending Payment Error message, "
- + "trying to queue task and add to employee handle..");
- employeeHandleIssue(o);
- }
+ handlePaymentPossibleErrorMsgErrorIssue(order, o);
};
var r = new Retry<>(op, handleError, numOfRetries, retryDuration,
e -> DatabaseUnavailableException.class.isAssignableFrom(e.getClass()));
@@ -468,6 +462,39 @@ public class Commander {
t.start();
}
+ private void handlePaymentPossibleErrorMsgErrorIssue(Order order, Order o) {
+ if (o.messageSent.equals(MessageSent.NONE_SENT) && order.paid
+ .equals(PaymentStatus.TRYING)
+ && System.currentTimeMillis() - o.createdTime < messageTime) {
+ var qt = new QueueTask(order, TaskType.MESSAGING, 1);
+ updateQueue(qt);
+ LOG.warn("Order " + order.id + ": Error in sending Payment Error message, "
+ + "trying to queue task and add to employee handle..");
+ employeeHandleIssue(o);
+ }
+ }
+
+ private void handlePaymentPossibleErrorMsgRetryOperation(Order order, List l)
+ throws Exception {
+ if (!l.isEmpty()) {
+ if (DatabaseUnavailableException.class.isAssignableFrom(l.get(0).getClass())) {
+ LOG.debug("Order " + order.id + ": Error in connecting to messaging service "
+ + "(Payment Error msg), trying again..");
+ } else {
+ LOG.debug("Order " + order.id + ": Error in creating Payment Error"
+ + " messaging request..");
+ }
+ throw l.remove(0);
+ }
+ if (order.paid.equals(PaymentStatus.TRYING) && order.messageSent
+ .equals(MessageSent.NONE_SENT)) {
+ var requestId = messagingService.receiveRequest(1);
+ order.messageSent = MessageSent.PAYMENT_TRYING;
+ LOG.info("Order " + order.id + ": Payment Error message sent successfully,"
+ + " request Id: " + requestId);
+ }
+ }
+
private void employeeHandleIssue(Order order) {
if (System.currentTimeMillis() - order.createdTime >= this.employeeTime) {
LOG.trace("Order " + order.id + ": Employee handle time for order over, returning..");
@@ -490,7 +517,7 @@ public class Commander {
Retry.HandleErrorIssue handleError = (o, err) -> {
if (!o.addedToEmployeeHandle && System
.currentTimeMillis() - order.createdTime < employeeTime) {
- var qt = new QueueTask(order, TaskType.EmployeeDb, -1);
+ var qt = new QueueTask(order, TaskType.EMPLOYEE_DB, -1);
updateQueue(qt);
LOG.warn("Order " + order.id + ": Error in adding to employee db,"
+ " trying to queue task..");
@@ -520,21 +547,21 @@ public class Commander {
LOG.trace("Order " + qt.order.id + ": This queue task of type " + qt.getType()
+ " does not need to be done anymore (timeout), dequeue..");
} else {
- if (qt.taskType.equals(TaskType.Payment)) {
- if (!qt.order.paid.equals(PaymentStatus.Trying)) {
+ if (qt.taskType.equals(TaskType.PAYMENT)) {
+ if (!qt.order.paid.equals(PaymentStatus.TRYING)) {
tryDequeue();
LOG.trace("Order " + qt.order.id + ": This payment task already done, dequeueing..");
} else {
sendPaymentRequest(qt.order);
LOG.debug("Order " + qt.order.id + ": Trying to connect to payment service..");
}
- } else if (qt.taskType.equals(TaskType.Messaging)) {
- if (qt.order.messageSent.equals(MessageSent.PaymentFail)
- || qt.order.messageSent.equals(MessageSent.PaymentSuccessful)) {
+ } else if (qt.taskType.equals(TaskType.MESSAGING)) {
+ if (qt.order.messageSent.equals(MessageSent.PAYMENT_FAIL)
+ || qt.order.messageSent.equals(MessageSent.PAYMENT_SUCCESSFUL)) {
tryDequeue();
LOG.trace("Order " + qt.order.id + ": This messaging task already done, dequeue..");
- } else if (qt.messageType == 1 && (!qt.order.messageSent.equals(MessageSent.NoneSent)
- || !qt.order.paid.equals(PaymentStatus.Trying))) {
+ } else if (qt.messageType == 1 && (!qt.order.messageSent.equals(MessageSent.NONE_SENT)
+ || !qt.order.paid.equals(PaymentStatus.TRYING))) {
tryDequeue();
LOG.trace("Order " + qt.order.id + ": This messaging task does not need to be done,"
+ " dequeue..");
@@ -548,7 +575,7 @@ public class Commander {
sendSuccessMessage(qt.order);
LOG.debug("Order " + qt.order.id + ": Trying to connect to messaging service..");
}
- } else if (qt.taskType.equals(TaskType.EmployeeDb)) {
+ } else if (qt.taskType.equals(TaskType.EMPLOYEE_DB)) {
if (qt.order.addedToEmployeeHandle) {
tryDequeue();
LOG.trace("Order " + qt.order.id + ": This employee handle task already done,"
diff --git a/commander/src/main/java/com/iluwatar/commander/Order.java b/commander/src/main/java/com/iluwatar/commander/Order.java
index 87a9f794a..f736aa47c 100644
--- a/commander/src/main/java/com/iluwatar/commander/Order.java
+++ b/commander/src/main/java/com/iluwatar/commander/Order.java
@@ -33,11 +33,11 @@ import java.util.Random;
public class Order { //can store all transactions ids also
enum PaymentStatus {
- NotDone, Trying, Done
+ NOT_DONE, TRYING, DONE
}
enum MessageSent {
- NoneSent, PaymentFail, PaymentTrying, PaymentSuccessful
+ NONE_SENT, PAYMENT_FAIL, PAYMENT_TRYING, PAYMENT_SUCCESSFUL
}
final User user;
@@ -65,8 +65,8 @@ public class Order { //can store all transactions ids also
}
this.id = id;
USED_IDS.put(this.id, true);
- this.paid = PaymentStatus.Trying;
- this.messageSent = MessageSent.NoneSent;
+ this.paid = PaymentStatus.TRYING;
+ this.messageSent = MessageSent.NONE_SENT;
this.addedToEmployeeHandle = false;
}
diff --git a/commander/src/main/java/com/iluwatar/commander/employeehandle/EmployeeDatabase.java b/commander/src/main/java/com/iluwatar/commander/employeehandle/EmployeeDatabase.java
index 496bb545a..69ebc1fd9 100644
--- a/commander/src/main/java/com/iluwatar/commander/employeehandle/EmployeeDatabase.java
+++ b/commander/src/main/java/com/iluwatar/commander/employeehandle/EmployeeDatabase.java
@@ -33,7 +33,7 @@ import java.util.Hashtable;
*/
public class EmployeeDatabase extends Database {
- private Hashtable data;
+ private final Hashtable data;
public EmployeeDatabase() {
this.data = new Hashtable<>();
diff --git a/commander/src/main/java/com/iluwatar/commander/messagingservice/MessagingDatabase.java b/commander/src/main/java/com/iluwatar/commander/messagingservice/MessagingDatabase.java
index fbba52cac..22ad733cb 100644
--- a/commander/src/main/java/com/iluwatar/commander/messagingservice/MessagingDatabase.java
+++ b/commander/src/main/java/com/iluwatar/commander/messagingservice/MessagingDatabase.java
@@ -33,7 +33,7 @@ import java.util.Hashtable;
*/
public class MessagingDatabase extends Database {
- private Hashtable data;
+ private final Hashtable data;
public MessagingDatabase() {
this.data = new Hashtable<>();
diff --git a/commander/src/main/java/com/iluwatar/commander/messagingservice/MessagingService.java b/commander/src/main/java/com/iluwatar/commander/messagingservice/MessagingService.java
index 3fb385757..e353a4c7c 100644
--- a/commander/src/main/java/com/iluwatar/commander/messagingservice/MessagingService.java
+++ b/commander/src/main/java/com/iluwatar/commander/messagingservice/MessagingService.java
@@ -38,7 +38,7 @@ public class MessagingService extends Service {
private static final Logger LOGGER = LoggerFactory.getLogger(MessagingService.class);
enum MessageToSend {
- PaymentFail, PaymentTrying, PaymentSuccessful
+ PAYMENT_FAIL, PAYMENT_TRYING, PAYMENT_SUCCESSFUL
}
class MessageRequest {
@@ -63,11 +63,11 @@ public class MessagingService extends Service {
var id = generateId();
MessageToSend msg;
if (messageToSend == 0) {
- msg = MessageToSend.PaymentFail;
+ msg = MessageToSend.PAYMENT_FAIL;
} else if (messageToSend == 1) {
- msg = MessageToSend.PaymentTrying;
+ msg = MessageToSend.PAYMENT_TRYING;
} else { //messageToSend == 2
- msg = MessageToSend.PaymentSuccessful;
+ msg = MessageToSend.PAYMENT_SUCCESSFUL;
}
var req = new MessageRequest(id, msg);
return updateDb(req);
@@ -84,10 +84,10 @@ public class MessagingService extends Service {
}
String sendMessage(MessageToSend m) {
- if (m.equals(MessageToSend.PaymentSuccessful)) {
+ if (m.equals(MessageToSend.PAYMENT_SUCCESSFUL)) {
return "Msg: Your order has been placed and paid for successfully!"
+ " Thank you for shopping with us!";
- } else if (m.equals(MessageToSend.PaymentTrying)) {
+ } else if (m.equals(MessageToSend.PAYMENT_TRYING)) {
return "Msg: There was an error in your payment process,"
+ " we are working on it and will return back to you shortly."
+ " Meanwhile, your order has been placed and will be shipped.";
diff --git a/commander/src/main/java/com/iluwatar/commander/paymentservice/PaymentDatabase.java b/commander/src/main/java/com/iluwatar/commander/paymentservice/PaymentDatabase.java
index 644979883..bf9e846bb 100644
--- a/commander/src/main/java/com/iluwatar/commander/paymentservice/PaymentDatabase.java
+++ b/commander/src/main/java/com/iluwatar/commander/paymentservice/PaymentDatabase.java
@@ -34,7 +34,7 @@ import java.util.Hashtable;
public class PaymentDatabase extends Database {
- private Hashtable data;
+ private final Hashtable data;
public PaymentDatabase() {
this.data = new Hashtable<>();
diff --git a/commander/src/main/java/com/iluwatar/commander/queue/QueueDatabase.java b/commander/src/main/java/com/iluwatar/commander/queue/QueueDatabase.java
index 91a7966f7..003a7da46 100644
--- a/commander/src/main/java/com/iluwatar/commander/queue/QueueDatabase.java
+++ b/commander/src/main/java/com/iluwatar/commander/queue/QueueDatabase.java
@@ -35,7 +35,7 @@ import java.util.List;
public class QueueDatabase extends Database {
- private Queue data;
+ private final Queue data;
public List exceptionsList;
public QueueDatabase(Exception... exc) {
diff --git a/commander/src/main/java/com/iluwatar/commander/queue/QueueTask.java b/commander/src/main/java/com/iluwatar/commander/queue/QueueTask.java
index a27dd62b8..341eb628c 100644
--- a/commander/src/main/java/com/iluwatar/commander/queue/QueueTask.java
+++ b/commander/src/main/java/com/iluwatar/commander/queue/QueueTask.java
@@ -36,7 +36,7 @@ public class QueueTask {
*/
public enum TaskType {
- Messaging, Payment, EmployeeDb
+ MESSAGING, PAYMENT, EMPLOYEE_DB
}
public Order order;
@@ -68,7 +68,7 @@ public class QueueTask {
* @return String representing type of task
*/
public String getType() {
- if (!this.taskType.equals(TaskType.Messaging)) {
+ if (!this.taskType.equals(TaskType.MESSAGING)) {
return this.taskType.toString();
} else {
if (this.messageType == 0) {
diff --git a/commander/src/main/java/com/iluwatar/commander/shippingservice/ShippingDatabase.java b/commander/src/main/java/com/iluwatar/commander/shippingservice/ShippingDatabase.java
index 305122db2..abaf27c9d 100644
--- a/commander/src/main/java/com/iluwatar/commander/shippingservice/ShippingDatabase.java
+++ b/commander/src/main/java/com/iluwatar/commander/shippingservice/ShippingDatabase.java
@@ -34,7 +34,7 @@ import java.util.Hashtable;
public class ShippingDatabase extends Database {
- private Hashtable data;
+ private final Hashtable data;
public ShippingDatabase() {
this.data = new Hashtable<>();
diff --git a/composite/README.md b/composite/README.md
index 25b553b76..b7aaa69df 100644
--- a/composite/README.md
+++ b/composite/README.md
@@ -9,15 +9,17 @@ tags:
---
## Intent
-Compose objects into tree structures to represent part-whole
-hierarchies. Composite lets clients treat individual objects and compositions
-of objects uniformly.
+
+Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients
+treat individual objects and compositions of objects uniformly.
## Explanation
Real world example
-> Every sentence is composed of words which are in turn composed of characters. Each of these objects is printable and they can have something printed before or after them like sentence always ends with full stop and word always has space before it
+> Every sentence is composed of words which are in turn composed of characters. Each of these
+> objects is printable and they can have something printed before or after them like sentence always
+> ends with full stop and word always has space before it.
In plain words
@@ -25,16 +27,21 @@ In plain words
Wikipedia says
-> In software engineering, the composite pattern is a partitioning design pattern. The composite pattern describes that a group of objects is to be treated in the same way as a single instance of an object. The intent of a composite is to "compose" objects into tree structures to represent part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects and compositions uniformly.
+> In software engineering, the composite pattern is a partitioning design pattern. The composite
+> pattern describes that a group of objects is to be treated in the same way as a single instance of
+> an object. The intent of a composite is to "compose" objects into tree structures to represent
+> part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects
+> and compositions uniformly.
**Programmatic Example**
-Taking our sentence example from above. Here we have the base class and different printable types
+Taking our sentence example from above. Here we have the base class `LetterComposite` and the
+different printable types `Letter`, `Word` and `Sentence`.
```java
public abstract class LetterComposite {
- private List children = new ArrayList<>();
+ private final List children = new ArrayList<>();
public void add(LetterComposite letter) {
children.add(letter);
@@ -59,7 +66,7 @@ public abstract class LetterComposite {
public class Letter extends LetterComposite {
- private char character;
+ private final char character;
public Letter(char c) {
this.character = c;
@@ -102,7 +109,7 @@ public class Sentence extends LetterComposite {
}
```
-Then we have a messenger to carry messages
+Then we have a messenger to carry messages:
```java
public class Messenger {
@@ -143,7 +150,7 @@ public class Messenger {
}
```
-And then it can be used as
+And then it can be used as:
```java
var orcMessage = new Messenger().messageFromOrcs();
@@ -153,13 +160,16 @@ elfMessage.print(); // Much wind pours from your mouth.
```
## Class diagram
+

## Applicability
+
Use the Composite pattern when
-* you want to represent part-whole hierarchies of objects
-* you want clients to be able to ignore the difference between compositions of objects and individual objects. Clients will treat all objects in the composite structure uniformly
+* You want to represent part-whole hierarchies of objects.
+* You want clients to be able to ignore the difference between compositions of objects and
+individual objects. Clients will treat all objects in the composite structure uniformly.
## Real world examples
diff --git a/composite/pom.xml b/composite/pom.xml
index c16b95c13..6f7147482 100644
--- a/composite/pom.xml
+++ b/composite/pom.xml
@@ -29,7 +29,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
composite
diff --git a/composite/src/main/java/com/iluwatar/composite/Letter.java b/composite/src/main/java/com/iluwatar/composite/Letter.java
index ab2d496ea..00b1a9639 100644
--- a/composite/src/main/java/com/iluwatar/composite/Letter.java
+++ b/composite/src/main/java/com/iluwatar/composite/Letter.java
@@ -28,7 +28,7 @@ package com.iluwatar.composite;
*/
public class Letter extends LetterComposite {
- private char character;
+ private final char character;
public Letter(char c) {
this.character = c;
diff --git a/composite/src/main/java/com/iluwatar/composite/LetterComposite.java b/composite/src/main/java/com/iluwatar/composite/LetterComposite.java
index 25808c468..0daf88222 100644
--- a/composite/src/main/java/com/iluwatar/composite/LetterComposite.java
+++ b/composite/src/main/java/com/iluwatar/composite/LetterComposite.java
@@ -1,58 +1,58 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.composite;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Composite interface.
- */
-public abstract class LetterComposite {
-
- private List children = new ArrayList<>();
-
- public void add(LetterComposite letter) {
- children.add(letter);
- }
-
- public int count() {
- return children.size();
- }
-
- protected void printThisBefore() {
- }
-
- protected void printThisAfter() {
- }
-
- /**
- * Print.
- */
- public void print() {
- printThisBefore();
- children.forEach(LetterComposite::print);
- printThisAfter();
- }
-}
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.composite;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Composite interface.
+ */
+public abstract class LetterComposite {
+
+ private final List children = new ArrayList<>();
+
+ public void add(LetterComposite letter) {
+ children.add(letter);
+ }
+
+ public int count() {
+ return children.size();
+ }
+
+ protected void printThisBefore() {
+ }
+
+ protected void printThisAfter() {
+ }
+
+ /**
+ * Print.
+ */
+ public void print() {
+ printThisBefore();
+ children.forEach(LetterComposite::print);
+ printThisAfter();
+ }
+}
diff --git a/composite/src/main/java/com/iluwatar/composite/module-info.java b/composite/src/main/java/com/iluwatar/composite/module-info.java
deleted file mode 100644
index d75a7b8f8..000000000
--- a/composite/src/main/java/com/iluwatar/composite/module-info.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.
- */
-
-module com.iluwatar.composite {
- requires org.slf4j;
-}
\ No newline at end of file
diff --git a/composite/src/test/java/com/iluwatar/composite/AppTest.java b/composite/src/test/java/com/iluwatar/composite/AppTest.java
index 5eb8c35c7..c82056a51 100644
--- a/composite/src/test/java/com/iluwatar/composite/AppTest.java
+++ b/composite/src/test/java/com/iluwatar/composite/AppTest.java
@@ -23,15 +23,23 @@
package com.iluwatar.composite;
+import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
/**
* Application test
*/
-public class AppTest {
+class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
+ * throws an exception.
+ */
@Test
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ Assertions.assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/converter/README.md b/converter/README.md
index 3be8ce331..02b172149 100644
--- a/converter/README.md
+++ b/converter/README.md
@@ -9,16 +9,19 @@ tags:
---
## Intent
-The purpose of the Converter Pattern is to provide a generic, common way of bidirectional
+
+The purpose of the Converter pattern is to provide a generic, common way of bidirectional
conversion between corresponding types, allowing a clean implementation in which the types do not
-need to be aware of each other. Moreover, the Converter Pattern introduces bidirectional collection
+need to be aware of each other. Moreover, the Converter pattern introduces bidirectional collection
mapping, reducing a boilerplate code to minimum.
## Explanation
Real world example
-> In real world applications it is often the case that database layer consists of entities that need to be mapped into DTOs for use on the business logic layer. Similar mapping is done for potentially huge amount of classes and we need a generic way to achieve this.
+> In real world applications it is often the case that database layer consists of entities that need
+> to be mapped into DTOs for use on the business logic layer. Similar mapping is done for
+> potentially huge amount of classes and we need a generic way to achieve this.
In plain words
@@ -26,7 +29,8 @@ In plain words
**Programmatic Example**
-We need a generic solution for the mapping problem. To achieve this, let's introduce a generic converter.
+We need a generic solution for the mapping problem. To achieve this, let's introduce a generic
+converter.
```java
public class Converter {
@@ -77,7 +81,7 @@ public class UserConverter extends Converter {
}
```
-Now mapping between User and UserDto becomes trivial.
+Now mapping between `User` and `UserDto` becomes trivial.
```java
var userConverter = new UserConverter();
@@ -86,14 +90,18 @@ var user = userConverter.convertFromDto(dtoUser);
```
## Class diagram
+

## Applicability
+
Use the Converter Pattern in the following situations:
-* When you have types that logically correspond which other and you need to convert entities between them
-* When you want to provide different ways of types conversions depending on a context
-* Whenever you introduce a DTO (Data transfer object), you will probably need to convert it into the domain equivalence
+* When you have types that logically correspond with each other and you need to convert entities
+between them.
+* When you want to provide different ways of types conversions depending on the context.
+* Whenever you introduce a DTO (Data transfer object), you will probably need to convert it into the
+domain equivalence.
## Credits
diff --git a/converter/pom.xml b/converter/pom.xml
index 56eb2ccdb..1bed5e973 100644
--- a/converter/pom.xml
+++ b/converter/pom.xml
@@ -20,7 +20,7 @@
java-design-patterns
com.iluwatar
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
converter
4.0.0
diff --git a/converter/src/main/java/com/iluwatar/converter/User.java b/converter/src/main/java/com/iluwatar/converter/User.java
index 637d77a25..2c1ba9ff0 100644
--- a/converter/src/main/java/com/iluwatar/converter/User.java
+++ b/converter/src/main/java/com/iluwatar/converter/User.java
@@ -29,10 +29,10 @@ import java.util.Objects;
* User class.
*/
public class User {
- private String firstName;
- private String lastName;
- private boolean isActive;
- private String userId;
+ private final String firstName;
+ private final String lastName;
+ private final boolean isActive;
+ private final String userId;
/**
* Constructor.
diff --git a/converter/src/main/java/com/iluwatar/converter/UserDto.java b/converter/src/main/java/com/iluwatar/converter/UserDto.java
index e75aaab8c..67a886087 100644
--- a/converter/src/main/java/com/iluwatar/converter/UserDto.java
+++ b/converter/src/main/java/com/iluwatar/converter/UserDto.java
@@ -30,10 +30,10 @@ import java.util.Objects;
*/
public class UserDto {
- private String firstName;
- private String lastName;
- private boolean isActive;
- private String email;
+ private final String firstName;
+ private final String lastName;
+ private final boolean isActive;
+ private final String email;
/**
* Constructor.
diff --git a/converter/src/main/java/com/iluwatar/converter/module-info.java b/converter/src/main/java/com/iluwatar/converter/module-info.java
deleted file mode 100644
index d83a43c6b..000000000
--- a/converter/src/main/java/com/iluwatar/converter/module-info.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.
- */
-
-module com.iluwatar.converter {
- requires org.slf4j;
-}
\ No newline at end of file
diff --git a/converter/src/test/java/com/iluwatar/converter/AppTest.java b/converter/src/test/java/com/iluwatar/converter/AppTest.java
index ed53c6863..7a99fe6ae 100644
--- a/converter/src/test/java/com/iluwatar/converter/AppTest.java
+++ b/converter/src/test/java/com/iluwatar/converter/AppTest.java
@@ -25,14 +25,24 @@ package com.iluwatar.converter;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* App running test
*/
-public class AppTest {
+class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
+ * throws an exception.
+ */
@Test
- public void testMain() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/converter/src/test/java/com/iluwatar/converter/ConverterTest.java b/converter/src/test/java/com/iluwatar/converter/ConverterTest.java
index d9e4e418b..46aca82a7 100644
--- a/converter/src/test/java/com/iluwatar/converter/ConverterTest.java
+++ b/converter/src/test/java/com/iluwatar/converter/ConverterTest.java
@@ -34,7 +34,7 @@ import org.junit.jupiter.api.Test;
*/
public class ConverterTest {
- private UserConverter userConverter = new UserConverter();
+ private final UserConverter userConverter = new UserConverter();
/**
* Tests whether a converter created of opposite functions holds equality as a bijection.
diff --git a/cqrs/pom.xml b/cqrs/pom.xml
index b3a0303e6..1838ed599 100644
--- a/cqrs/pom.xml
+++ b/cqrs/pom.xml
@@ -30,7 +30,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
cqrs
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/commandes/CommandServiceImpl.java b/cqrs/src/main/java/com/iluwatar/cqrs/commandes/CommandServiceImpl.java
index ba08811e7..e402adad8 100644
--- a/cqrs/src/main/java/com/iluwatar/cqrs/commandes/CommandServiceImpl.java
+++ b/cqrs/src/main/java/com/iluwatar/cqrs/commandes/CommandServiceImpl.java
@@ -34,7 +34,7 @@ import org.hibernate.SessionFactory;
*/
public class CommandServiceImpl implements ICommandService {
- private SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
+ private final SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
private Author getAuthorByUsername(String username) {
Author author;
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/queries/QueryServiceImpl.java b/cqrs/src/main/java/com/iluwatar/cqrs/queries/QueryServiceImpl.java
index 9b008402e..d30c0f386 100644
--- a/cqrs/src/main/java/com/iluwatar/cqrs/queries/QueryServiceImpl.java
+++ b/cqrs/src/main/java/com/iluwatar/cqrs/queries/QueryServiceImpl.java
@@ -38,7 +38,7 @@ import org.hibernate.transform.Transformers;
*/
public class QueryServiceImpl implements IQueryService {
- private SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
+ private final SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
@Override
public Author getAuthorByUsername(String username) {
diff --git a/dao/README.md b/dao/README.md
index 4b65679c4..29e64a74f 100644
--- a/dao/README.md
+++ b/dao/README.md
@@ -9,13 +9,15 @@ tags:
---
## Intent
+
Object provides an abstract interface to some type of database or other persistence mechanism.
## Explanation
Real world example
-> There's a set of customers that need to be persisted to database. Additionally we need the whole set of CRUD (create/read/update/delete) operations so we can operate on customers easily.
+> There's a set of customers that need to be persisted to database. Additionally we need the whole
+> set of CRUD (create/read/update/delete) operations so we can operate on customers easily.
In plain words
@@ -23,11 +25,12 @@ In plain words
Wikipedia says
-> In computer software, a data access object (DAO) is a pattern that provides an abstract interface to some type of database or other persistence mechanism.
+> In computer software, a data access object (DAO) is a pattern that provides an abstract interface
+> to some type of database or other persistence mechanism.
**Programmatic Example**
-Walking through our customers example, here's the basic Customer entity.
+Walking through our customers example, here's the basic `Customer` entity.
```java
public class Customer {
@@ -41,60 +44,13 @@ public class Customer {
this.firstName = firstName;
this.lastName = lastName;
}
-
- public int getId() {
- return id;
- }
-
- public void setId(final int id) {
- this.id = id;
- }
-
- public String getFirstName() {
- return firstName;
- }
-
- public void setFirstName(final String firstName) {
- this.firstName = firstName;
- }
-
- public String getLastName() {
- return lastName;
- }
-
- public void setLastName(final String lastName) {
- this.lastName = lastName;
- }
-
- @Override
- public String toString() {
- return "Customer{" + "id=" + getId() + ", firstName='" + getFirstName() + '\'' + ", lastName='"
- + getLastName() + '\'' + '}';
- }
-
- @Override
- public boolean equals(final Object that) {
- var isEqual = false;
- if (this == that) {
- isEqual = true;
- } else if (that != null && getClass() == that.getClass()) {
- final var customer = (Customer) that;
- if (getId() == customer.getId()) {
- isEqual = true;
- }
- }
- return isEqual;
- }
-
- @Override
- public int hashCode() {
- return getId();
- }
+ // getters and setters ->
+ ...
}
```
-Here's the DAO interface and two different implementations for it. InMemoryCustomerDao keeps a simple map of customers
-in memory while DBCustomerDao is the real RDBMS implementation.
+Here's the `CustomerDao` interface and two different implementations for it. `InMemoryCustomerDao`
+keeps a simple map of customers in memory while `DBCustomerDao` is the real RDBMS implementation.
```java
public interface CustomerDao {
@@ -112,37 +68,10 @@ public interface CustomerDao {
public class InMemoryCustomerDao implements CustomerDao {
- private Map idToCustomer = new HashMap<>();
+ private final Map idToCustomer = new HashMap<>();
- @Override
- public Stream getAll() {
- return idToCustomer.values().stream();
- }
-
- @Override
- public Optional getById(final int id) {
- return Optional.ofNullable(idToCustomer.get(id));
- }
-
- @Override
- public boolean add(final Customer customer) {
- if (getById(customer.getId()).isPresent()) {
- return false;
- }
-
- idToCustomer.put(customer.getId(), customer);
- return true;
- }
-
- @Override
- public boolean update(final Customer customer) {
- return idToCustomer.replace(customer.getId(), customer) != null;
- }
-
- @Override
- public boolean delete(final Customer customer) {
- return idToCustomer.remove(customer.getId()) != null;
- }
+ // implement the interface using the map
+ ...
}
public class DbCustomerDao implements CustomerDao {
@@ -155,121 +84,8 @@ public class DbCustomerDao implements CustomerDao {
this.dataSource = dataSource;
}
- @Override
- public Stream getAll() throws Exception {
- try {
- var connection = getConnection();
- var statement = connection.prepareStatement("SELECT * FROM CUSTOMERS");
- var resultSet = statement.executeQuery();
- return StreamSupport.stream(new Spliterators.AbstractSpliterator(Long.MAX_VALUE,
- Spliterator.ORDERED) {
-
- @Override
- public boolean tryAdvance(Consumer super Customer> action) {
- try {
- if (!resultSet.next()) {
- return false;
- }
- action.accept(createCustomer(resultSet));
- return true;
- } catch (SQLException e) {
- throw new RuntimeException(e);
- }
- }
- }, false).onClose(() -> mutedClose(connection, statement, resultSet));
- } catch (SQLException e) {
- throw new CustomException(e.getMessage(), e);
- }
- }
-
- private Connection getConnection() throws SQLException {
- return dataSource.getConnection();
- }
-
- private void mutedClose(Connection connection, PreparedStatement statement, ResultSet resultSet) {
- try {
- resultSet.close();
- statement.close();
- connection.close();
- } catch (SQLException e) {
- LOGGER.info("Exception thrown " + e.getMessage());
- }
- }
-
- private Customer createCustomer(ResultSet resultSet) throws SQLException {
- return new Customer(resultSet.getInt("ID"),
- resultSet.getString("FNAME"),
- resultSet.getString("LNAME"));
- }
-
- @Override
- public Optional getById(int id) throws Exception {
-
- ResultSet resultSet = null;
-
- try (var connection = getConnection();
- var statement = connection.prepareStatement("SELECT * FROM CUSTOMERS WHERE ID = ?")) {
-
- statement.setInt(1, id);
- resultSet = statement.executeQuery();
- if (resultSet.next()) {
- return Optional.of(createCustomer(resultSet));
- } else {
- return Optional.empty();
- }
- } catch (SQLException ex) {
- throw new CustomException(ex.getMessage(), ex);
- } finally {
- if (resultSet != null) {
- resultSet.close();
- }
- }
- }
-
- @Override
- public boolean add(Customer customer) throws Exception {
- if (getById(customer.getId()).isPresent()) {
- return false;
- }
-
- try (var connection = getConnection();
- var statement = connection.prepareStatement("INSERT INTO CUSTOMERS VALUES (?,?,?)")) {
- statement.setInt(1, customer.getId());
- statement.setString(2, customer.getFirstName());
- statement.setString(3, customer.getLastName());
- statement.execute();
- return true;
- } catch (SQLException ex) {
- throw new CustomException(ex.getMessage(), ex);
- }
- }
-
- @Override
- public boolean update(Customer customer) throws Exception {
- try (var connection = getConnection();
- var statement =
- connection
- .prepareStatement("UPDATE CUSTOMERS SET FNAME = ?, LNAME = ? WHERE ID = ?")) {
- statement.setString(1, customer.getFirstName());
- statement.setString(2, customer.getLastName());
- statement.setInt(3, customer.getId());
- return statement.executeUpdate() > 0;
- } catch (SQLException ex) {
- throw new CustomException(ex.getMessage(), ex);
- }
- }
-
- @Override
- public boolean delete(Customer customer) throws Exception {
- try (var connection = getConnection();
- var statement = connection.prepareStatement("DELETE FROM CUSTOMERS WHERE ID = ?")) {
- statement.setInt(1, customer.getId());
- return statement.executeUpdate() > 0;
- } catch (SQLException ex) {
- throw new CustomException(ex.getMessage(), ex);
- }
- }
-}
+ // implement the interface using the data source
+ ...
```
Finally here's how we use our DAO to manage customers.
@@ -301,15 +117,45 @@ Finally here's how we use our DAO to manage customers.
deleteSchema(dataSource);
```
+The program output:
+
+```java
+customerDao.getAllCustomers():
+Customer{id=1, firstName='Adam', lastName='Adamson'}
+Customer{id=2, firstName='Bob', lastName='Bobson'}
+Customer{id=3, firstName='Carl', lastName='Carlson'}
+customerDao.getCustomerById(2): Optional[Customer{id=2, firstName='Bob', lastName='Bobson'}]
+customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@7cef4e59
+customerDao.getAllCustomers():
+Customer{id=1, firstName='Adam', lastName='Adamson'}
+Customer{id=2, firstName='Bob', lastName='Bobson'}
+Customer{id=3, firstName='Carl', lastName='Carlson'}
+Customer{id=4, firstName='Daniel', lastName='Danielson'}
+customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@2db0f6b2
+customerDao.getAllCustomers():
+Customer{id=1, firstName='Adam', lastName='Adamson'}
+Customer{id=2, firstName='Bob', lastName='Bobson'}
+Customer{id=3, firstName='Carl', lastName='Carlson'}
+customerDao.getCustomerById(2): Optional[Customer{id=2, firstName='Bob', lastName='Bobson'}]
+customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@12c8a2c0
+customerDao.getAllCustomers():
+Customer{id=1, firstName='Adam', lastName='Adamson'}
+Customer{id=2, firstName='Bob', lastName='Bobson'}
+Customer{id=3, firstName='Carl', lastName='Carlson'}
+Customer{id=4, firstName='Daniel', lastName='Danielson'}
+customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@6ec8211c
+```
## Class diagram
+

## Applicability
-Use the Data Access Object in any of the following situations
-* when you want to consolidate how the data layer is accessed
-* when you want to avoid writing multiple data retrieval/persistence layers
+Use the Data Access Object in any of the following situations:
+
+* When you want to consolidate how the data layer is accessed.
+* When you want to avoid writing multiple data retrieval/persistence layers.
## Credits
diff --git a/dao/pom.xml b/dao/pom.xml
index 7e8bd5625..32e9ef1ff 100644
--- a/dao/pom.xml
+++ b/dao/pom.xml
@@ -30,7 +30,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
dao
diff --git a/dao/src/main/java/com/iluwatar/dao/App.java b/dao/src/main/java/com/iluwatar/dao/App.java
index de9c7b7c1..6d578bc79 100644
--- a/dao/src/main/java/com/iluwatar/dao/App.java
+++ b/dao/src/main/java/com/iluwatar/dao/App.java
@@ -44,7 +44,7 @@ import org.slf4j.LoggerFactory;
*/
public class App {
private static final String DB_URL = "jdbc:h2:~/dao";
- private static Logger log = LoggerFactory.getLogger(App.class);
+ private static final Logger log = LoggerFactory.getLogger(App.class);
private static final String ALL_CUSTOMERS = "customerDao.getAllCustomers(): ";
/**
diff --git a/dao/src/main/java/com/iluwatar/dao/InMemoryCustomerDao.java b/dao/src/main/java/com/iluwatar/dao/InMemoryCustomerDao.java
index 6dbfa367a..0a3bd40e3 100644
--- a/dao/src/main/java/com/iluwatar/dao/InMemoryCustomerDao.java
+++ b/dao/src/main/java/com/iluwatar/dao/InMemoryCustomerDao.java
@@ -36,7 +36,7 @@ import java.util.stream.Stream;
*/
public class InMemoryCustomerDao implements CustomerDao {
- private Map idToCustomer = new HashMap<>();
+ private final Map idToCustomer = new HashMap<>();
/**
* An eagerly evaluated stream of customers stored in memory.
diff --git a/dao/src/main/java/com/iluwatar/dao/module-info.java b/dao/src/main/java/com/iluwatar/dao/module-info.java
deleted file mode 100644
index 08e4f662e..000000000
--- a/dao/src/main/java/com/iluwatar/dao/module-info.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.
- */
-
-module com.iluwatar.dao {
- requires org.slf4j;
- requires java.sql;
- requires h2;
- requires java.naming;
-}
\ No newline at end of file
diff --git a/dao/src/test/java/com/iluwatar/dao/AppTest.java b/dao/src/test/java/com/iluwatar/dao/AppTest.java
index edfcf7cd0..e6d41fc8a 100644
--- a/dao/src/test/java/com/iluwatar/dao/AppTest.java
+++ b/dao/src/test/java/com/iluwatar/dao/AppTest.java
@@ -25,12 +25,22 @@ package com.iluwatar.dao;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Tests that DAO example runs without errors.
*/
-public class AppTest {
+class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
+ * throws an exception.
+ */
+
@Test
- public void test() throws Exception {
- App.main(new String[]{});
+ void shouldExecuteDaoWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/dao/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java b/dao/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java
index b7a0b9769..8155cda79 100644
--- a/dao/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java
+++ b/dao/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java
@@ -50,7 +50,7 @@ public class DbCustomerDaoTest {
private static final String DB_URL = "jdbc:h2:~/dao";
private DbCustomerDao dao;
- private Customer existingCustomer = new Customer(1, "Freddy", "Krueger");
+ private final Customer existingCustomer = new Customer(1, "Freddy", "Krueger");
/**
* Creates customers schema.
diff --git a/data-bus/pom.xml b/data-bus/pom.xml
index e67135ae0..4db738307 100644
--- a/data-bus/pom.xml
+++ b/data-bus/pom.xml
@@ -33,7 +33,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
data-bus
diff --git a/data-bus/src/main/java/com/iluwatar/databus/members/MessageCollectorMember.java b/data-bus/src/main/java/com/iluwatar/databus/members/MessageCollectorMember.java
index 5a8218225..d77d56b9f 100644
--- a/data-bus/src/main/java/com/iluwatar/databus/members/MessageCollectorMember.java
+++ b/data-bus/src/main/java/com/iluwatar/databus/members/MessageCollectorMember.java
@@ -41,7 +41,7 @@ public class MessageCollectorMember implements Member {
private final String name;
- private List messages = new ArrayList<>();
+ private final List messages = new ArrayList<>();
public MessageCollectorMember(String name) {
this.name = name;
diff --git a/data-locality/pom.xml b/data-locality/pom.xml
index 660daa9b7..88fd96c64 100644
--- a/data-locality/pom.xml
+++ b/data-locality/pom.xml
@@ -30,7 +30,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
data-locality
diff --git a/data-locality/src/main/java/com/iluwatar/data/locality/game/component/AiComponent.java b/data-locality/src/main/java/com/iluwatar/data/locality/game/component/AiComponent.java
index 5b1be9e35..40acb2f71 100644
--- a/data-locality/src/main/java/com/iluwatar/data/locality/game/component/AiComponent.java
+++ b/data-locality/src/main/java/com/iluwatar/data/locality/game/component/AiComponent.java
@@ -43,6 +43,6 @@ public class AiComponent implements Component {
@Override
public void render() {
-
+ // Do Nothing.
}
}
diff --git a/data-locality/src/main/java/com/iluwatar/data/locality/game/component/manager/AiComponentManager.java b/data-locality/src/main/java/com/iluwatar/data/locality/game/component/manager/AiComponentManager.java
index 616ebf801..c85bd1e68 100644
--- a/data-locality/src/main/java/com/iluwatar/data/locality/game/component/manager/AiComponentManager.java
+++ b/data-locality/src/main/java/com/iluwatar/data/locality/game/component/manager/AiComponentManager.java
@@ -40,7 +40,7 @@ public class AiComponentManager {
private final int numEntities;
- private static final Component[] AI_COMPONENTS = new AiComponent[MAX_ENTITIES];
+ private final Component[] aiComponents = new AiComponent[MAX_ENTITIES];
public AiComponentManager(int numEntities) {
this.numEntities = numEntities;
@@ -51,7 +51,7 @@ public class AiComponentManager {
*/
public void start() {
LOGGER.info("Start AI Game Component");
- IntStream.range(0, numEntities).forEach(i -> AI_COMPONENTS[i] = new AiComponent());
+ IntStream.range(0, numEntities).forEach(i -> aiComponents[i] = new AiComponent());
}
/**
@@ -60,7 +60,7 @@ public class AiComponentManager {
public void update() {
LOGGER.info("Update AI Game Component");
IntStream.range(0, numEntities)
- .filter(i -> AI_COMPONENTS.length > i && AI_COMPONENTS[i] != null)
- .forEach(i -> AI_COMPONENTS[i].update());
+ .filter(i -> aiComponents.length > i && aiComponents[i] != null)
+ .forEach(i -> aiComponents[i].update());
}
}
diff --git a/data-locality/src/main/java/com/iluwatar/data/locality/game/component/manager/PhysicsComponentManager.java b/data-locality/src/main/java/com/iluwatar/data/locality/game/component/manager/PhysicsComponentManager.java
index 61ba4ebdd..155793c88 100644
--- a/data-locality/src/main/java/com/iluwatar/data/locality/game/component/manager/PhysicsComponentManager.java
+++ b/data-locality/src/main/java/com/iluwatar/data/locality/game/component/manager/PhysicsComponentManager.java
@@ -40,7 +40,7 @@ public class PhysicsComponentManager {
private final int numEntities;
- private static final Component[] PHYSICS_COMPONENTS = new PhysicsComponent[MAX_ENTITIES];
+ private final Component[] physicsComponents = new PhysicsComponent[MAX_ENTITIES];
public PhysicsComponentManager(int numEntities) {
this.numEntities = numEntities;
@@ -51,7 +51,7 @@ public class PhysicsComponentManager {
*/
public void start() {
LOGGER.info("Start Physics Game Component ");
- IntStream.range(0, numEntities).forEach(i -> PHYSICS_COMPONENTS[i] = new PhysicsComponent());
+ IntStream.range(0, numEntities).forEach(i -> physicsComponents[i] = new PhysicsComponent());
}
@@ -62,7 +62,7 @@ public class PhysicsComponentManager {
LOGGER.info("Update Physics Game Component ");
// Process physics.
IntStream.range(0, numEntities)
- .filter(i -> PHYSICS_COMPONENTS.length > i && PHYSICS_COMPONENTS[i] != null)
- .forEach(i -> PHYSICS_COMPONENTS[i].update());
+ .filter(i -> physicsComponents.length > i && physicsComponents[i] != null)
+ .forEach(i -> physicsComponents[i].update());
}
}
diff --git a/data-locality/src/main/java/com/iluwatar/data/locality/game/component/manager/RenderComponentManager.java b/data-locality/src/main/java/com/iluwatar/data/locality/game/component/manager/RenderComponentManager.java
index f8c4b3522..be1d3c2e9 100644
--- a/data-locality/src/main/java/com/iluwatar/data/locality/game/component/manager/RenderComponentManager.java
+++ b/data-locality/src/main/java/com/iluwatar/data/locality/game/component/manager/RenderComponentManager.java
@@ -40,7 +40,7 @@ public class RenderComponentManager {
private final int numEntities;
- private static final Component[] RENDER_COMPONENTS = new RenderComponent[MAX_ENTITIES];
+ private final Component[] renderComponents = new RenderComponent[MAX_ENTITIES];
public RenderComponentManager(int numEntities) {
this.numEntities = numEntities;
@@ -51,7 +51,7 @@ public class RenderComponentManager {
*/
public void start() {
LOGGER.info("Start Render Game Component ");
- IntStream.range(0, numEntities).forEach(i -> RENDER_COMPONENTS[i] = new RenderComponent());
+ IntStream.range(0, numEntities).forEach(i -> renderComponents[i] = new RenderComponent());
}
@@ -62,7 +62,7 @@ public class RenderComponentManager {
LOGGER.info("Update Render Game Component ");
// Process Render.
IntStream.range(0, numEntities)
- .filter(i -> RENDER_COMPONENTS.length > i && RENDER_COMPONENTS[i] != null)
- .forEach(i -> RENDER_COMPONENTS[i].render());
+ .filter(i -> renderComponents.length > i && renderComponents[i] != null)
+ .forEach(i -> renderComponents[i].render());
}
}
diff --git a/data-locality/src/test/java/com/iluwatar/data/locality/ApplicationTest.java b/data-locality/src/test/java/com/iluwatar/data/locality/ApplicationTest.java
index 3371be4c1..b7d1f8961 100644
--- a/data-locality/src/test/java/com/iluwatar/data/locality/ApplicationTest.java
+++ b/data-locality/src/test/java/com/iluwatar/data/locality/ApplicationTest.java
@@ -26,16 +26,22 @@ package com.iluwatar.data.locality;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Test Game Application
*/
class ApplicationTest {
/**
- * Test run
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link Application#main(String[])}
+ * throws an exception.
*/
+
@Test
- void main() {
- Application.main(new String[] {});
+ void shouldExecuteGameApplicationWithoutException() {
+ assertDoesNotThrow(() -> Application.main(new String[] {}));
}
}
\ No newline at end of file
diff --git a/data-mapper/pom.xml b/data-mapper/pom.xml
index 64f03a186..cf17de69b 100644
--- a/data-mapper/pom.xml
+++ b/data-mapper/pom.xml
@@ -28,7 +28,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
data-mapper
diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/App.java b/data-mapper/src/main/java/com/iluwatar/datamapper/App.java
index 9bfc32952..09c027401 100644
--- a/data-mapper/src/main/java/com/iluwatar/datamapper/App.java
+++ b/data-mapper/src/main/java/com/iluwatar/datamapper/App.java
@@ -1,83 +1,83 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.datamapper;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The Data Mapper (DM) is a layer of software that separates the in-memory objects from the
- * database. Its responsibility is to transfer data between the two and also to isolate them from
- * each other. With Data Mapper the in-memory objects needn't know even that there's a database
- * present; they need no SQL interface code, and certainly no knowledge of the database schema. (The
- * database schema is always ignorant of the objects that use it.) Since it's a form of Mapper ,
- * Data Mapper itself is even unknown to the domain layer.
- *
- * The below example demonstrates basic CRUD operations: Create, Read, Update, and Delete.
- */
-public final class App {
-
- private static Logger log = LoggerFactory.getLogger(App.class);
- private static final String STUDENT_STRING = "App.main(), student : ";
-
-
- /**
- * Program entry point.
- *
- * @param args command line args.
- */
- public static void main(final String... args) {
-
- /* Create new data mapper for type 'first' */
- final var mapper = new StudentDataMapperImpl();
-
- /* Create new student */
- var student = new Student(1, "Adam", 'A');
-
- /* Add student in respectibe store */
- mapper.insert(student);
-
- log.debug(STUDENT_STRING + student + ", is inserted");
-
- /* Find this student */
- final var studentToBeFound = mapper.find(student.getStudentId());
-
- log.debug(STUDENT_STRING + studentToBeFound + ", is searched");
-
- /* Update existing student object */
- student = new Student(student.getStudentId(), "AdamUpdated", 'A');
-
- /* Update student in respectibe db */
- mapper.update(student);
-
- log.debug(STUDENT_STRING + student + ", is updated");
- log.debug(STUDENT_STRING + student + ", is going to be deleted");
-
- /* Delete student in db */
- mapper.delete(student);
- }
-
- private App() {
- }
-}
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.datamapper;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The Data Mapper (DM) is a layer of software that separates the in-memory objects from the
+ * database. Its responsibility is to transfer data between the two and also to isolate them from
+ * each other. With Data Mapper the in-memory objects needn't know even that there's a database
+ * present; they need no SQL interface code, and certainly no knowledge of the database schema. (The
+ * database schema is always ignorant of the objects that use it.) Since it's a form of Mapper ,
+ * Data Mapper itself is even unknown to the domain layer.
+ *
+ *
The below example demonstrates basic CRUD operations: Create, Read, Update, and Delete.
+ */
+public final class App {
+
+ private static final Logger log = LoggerFactory.getLogger(App.class);
+ private static final String STUDENT_STRING = "App.main(), student : ";
+
+
+ /**
+ * Program entry point.
+ *
+ * @param args command line args.
+ */
+ public static void main(final String... args) {
+
+ /* Create new data mapper for type 'first' */
+ final var mapper = new StudentDataMapperImpl();
+
+ /* Create new student */
+ var student = new Student(1, "Adam", 'A');
+
+ /* Add student in respectibe store */
+ mapper.insert(student);
+
+ log.debug(STUDENT_STRING + student + ", is inserted");
+
+ /* Find this student */
+ final var studentToBeFound = mapper.find(student.getStudentId());
+
+ log.debug(STUDENT_STRING + studentToBeFound + ", is searched");
+
+ /* Update existing student object */
+ student = new Student(student.getStudentId(), "AdamUpdated", 'A');
+
+ /* Update student in respectibe db */
+ mapper.update(student);
+
+ log.debug(STUDENT_STRING + student + ", is updated");
+ log.debug(STUDENT_STRING + student + ", is going to be deleted");
+
+ /* Delete student in db */
+ mapper.delete(student);
+ }
+
+ private App() {
+ }
+}
diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapperImpl.java b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapperImpl.java
index 85ad4aa8d..7abe04e3f 100644
--- a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapperImpl.java
+++ b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapperImpl.java
@@ -33,7 +33,7 @@ import java.util.Optional;
public final class StudentDataMapperImpl implements StudentDataMapper {
/* Note: Normally this would be in the form of an actual database */
- private List students = new ArrayList<>();
+ private final List students = new ArrayList<>();
@Override
public Optional find(int studentId) {
diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/module-info.java b/data-mapper/src/main/java/com/iluwatar/datamapper/module-info.java
deleted file mode 100644
index 7abd78826..000000000
--- a/data-mapper/src/main/java/com/iluwatar/datamapper/module-info.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.
- */
-
-module com.iluwatar.datamapper {
- requires org.slf4j;
-}
\ No newline at end of file
diff --git a/data-mapper/src/test/java/com/iluwatar/datamapper/AppTest.java b/data-mapper/src/test/java/com/iluwatar/datamapper/AppTest.java
index ec1d71be4..ab74edd6c 100644
--- a/data-mapper/src/test/java/com/iluwatar/datamapper/AppTest.java
+++ b/data-mapper/src/test/java/com/iluwatar/datamapper/AppTest.java
@@ -24,14 +24,25 @@
package com.iluwatar.datamapper;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
/**
* Tests that Data-Mapper example runs without errors.
*/
-public final class AppTest {
+final class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
+ * throws an exception.
+ */
@Test
- public void test() {
- App.main();
+ void shouldExecuteApplicationWithoutException() {
+
+ assertDoesNotThrow((Executable) App::main);
}
}
diff --git a/data-transfer-object/README.md b/data-transfer-object/README.md
index e9286ce03..72e2e240e 100644
--- a/data-transfer-object/README.md
+++ b/data-transfer-object/README.md
@@ -9,13 +9,16 @@ tags:
---
## Intent
-Pass data with multiple attributes in one shot from client to server, to avoid multiple calls to remote server.
+
+Pass data with multiple attributes in one shot from client to server, to avoid multiple calls to
+remote server.
## Explanation
Real world example
-> We need to fetch information about customers from remote database. Instead of querying the attributes one at a time, we use DTOs to transfer all the relevant attributes in a single shot.
+> We need to fetch information about customers from remote database. Instead of querying the
+> attributes one at a time, we use DTOs to transfer all the relevant attributes in a single shot.
In plain words
@@ -23,16 +26,17 @@ In plain words
Wikipedia says
-> In the field of programming a data transfer object (DTO) is an object that carries data between processes. The
-motivation for its use is that communication between processes is usually done resorting to remote interfaces
-(e.g., web services), where each call is an expensive operation. Because the majority of the cost of each call is
-related to the round-trip time between the client and the server, one way of reducing the number of calls is to use an
-object (the DTO) that aggregates the data that would have been transferred by the several calls, but that is served by
-one call only.
+> In the field of programming a data transfer object (DTO) is an object that carries data between
+> processes. The motivation for its use is that communication between processes is usually done
+> resorting to remote interfaces (e.g. web services), where each call is an expensive operation.
+> Because the majority of the cost of each call is related to the round-trip time between the client
+> and the server, one way of reducing the number of calls is to use an object (the DTO) that
+> aggregates the data that would have been transferred by the several calls, but that is served by
+> one call only.
**Programmatic Example**
-Let's first introduce our simple customer DTO class.
+Let's first introduce our simple `CustomerDTO` class.
```java
public class CustomerDto {
@@ -60,11 +64,11 @@ public class CustomerDto {
}
```
-Customer resource class acts as the server for customer information.
+`CustomerResource` class acts as the server for customer information.
```java
public class CustomerResource {
- private List customers;
+ private final List customers;
public CustomerResource(List customers) {
this.customers = customers;
@@ -94,10 +98,12 @@ Now fetching customer information is easy since we have the DTOs.
```
## Class diagram
+

## Applicability
-Use the Data Transfer Object pattern when
+
+Use the Data Transfer Object pattern when:
* The client is asking for multiple information. And the information is related.
* When you want to boost the performance to get resources.
diff --git a/data-transfer-object/pom.xml b/data-transfer-object/pom.xml
index 459b1ab1e..2529b3756 100644
--- a/data-transfer-object/pom.xml
+++ b/data-transfer-object/pom.xml
@@ -28,7 +28,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
data-transfer-object
diff --git a/data-transfer-object/src/main/java/com/iluwatar/datatransfer/CustomerResource.java b/data-transfer-object/src/main/java/com/iluwatar/datatransfer/CustomerResource.java
index 7e4b8340d..d0a153f6f 100644
--- a/data-transfer-object/src/main/java/com/iluwatar/datatransfer/CustomerResource.java
+++ b/data-transfer-object/src/main/java/com/iluwatar/datatransfer/CustomerResource.java
@@ -30,7 +30,7 @@ import java.util.List;
* has all customer details.
*/
public class CustomerResource {
- private List customers;
+ private final List customers;
/**
* Initialise resource with existing customers.
diff --git a/data-transfer-object/src/main/java/com/iluwatar/datatransfer/module-info.java b/data-transfer-object/src/main/java/com/iluwatar/datatransfer/module-info.java
deleted file mode 100644
index 25685d4d0..000000000
--- a/data-transfer-object/src/main/java/com/iluwatar/datatransfer/module-info.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.
- */
-
-module com.iluwatar.datatransfer {
- requires org.slf4j;
-}
\ No newline at end of file
diff --git a/data-transfer-object/src/test/java/com/iluwatar/datatransfer/AppTest.java b/data-transfer-object/src/test/java/com/iluwatar/datatransfer/AppTest.java
index 3a58d0c54..68a8b9444 100644
--- a/data-transfer-object/src/test/java/com/iluwatar/datatransfer/AppTest.java
+++ b/data-transfer-object/src/test/java/com/iluwatar/datatransfer/AppTest.java
@@ -25,9 +25,19 @@ package com.iluwatar.datatransfer;
import org.junit.jupiter.api.Test;
-public class AppTest {
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
+ * throws an exception.
+ */
+
@Test
- public void test() throws Exception {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/decorator/README.md b/decorator/README.md
index a9dd5d745..7ac0bb94c 100644
--- a/decorator/README.md
+++ b/decorator/README.md
@@ -10,30 +10,39 @@ tags:
---
## Also known as
+
Wrapper
## Intent
-Attach additional responsibilities to an object dynamically.
-Decorators provide a flexible alternative to subclassing for extending
-functionality.
+
+Attach additional responsibilities to an object dynamically. Decorators provide a flexible
+alternative to subclassing for extending functionality.
## Explanation
Real world example
-> There is an angry troll living in the nearby hills. Usually it goes bare handed but sometimes it has a weapon. To arm the troll it's not necessary to create a new troll but to decorate it dynamically with a suitable weapon.
+> There is an angry troll living in the nearby hills. Usually it goes bare handed but sometimes it
+> has a weapon. To arm the troll it's not necessary to create a new troll but to decorate it
+> dynamically with a suitable weapon.
In plain words
-> Decorator pattern lets you dynamically change the behavior of an object at run time by wrapping them in an object of a decorator class.
+> Decorator pattern lets you dynamically change the behavior of an object at run time by wrapping
+> them in an object of a decorator class.
Wikipedia says
-> In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class. The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern.
+> In object-oriented programming, the decorator pattern is a design pattern that allows behavior to
+> be added to an individual object, either statically or dynamically, without affecting the behavior
+> of other objects from the same class. The decorator pattern is often useful for adhering to the
+> Single Responsibility Principle, as it allows functionality to be divided between classes with
+> unique areas of concern.
**Programmatic Example**
-Let's take the troll example. First of all we have a simple troll implementing the troll interface
+Let's take the troll example. First of all we have a `SimpleTroll` implementing the `Troll`
+interface:
```java
public interface Troll {
@@ -63,14 +72,14 @@ public class SimpleTroll implements Troll {
}
```
-Next we want to add club for the troll. We can do it dynamically by using a decorator
+Next we want to add club for the troll. We can do it dynamically by using a decorator:
```java
public class ClubbedTroll implements Troll {
private static final Logger LOGGER = LoggerFactory.getLogger(ClubbedTroll.class);
- private Troll decorated;
+ private final Troll decorated;
public ClubbedTroll(Troll decorated) {
this.decorated = decorated;
@@ -94,7 +103,7 @@ public class ClubbedTroll implements Troll {
}
```
-Here's the troll in action
+Here's the troll in action:
```java
// simple troll
@@ -108,20 +117,36 @@ clubbedTroll.attack(); // The troll tries to grab you! The troll swings at you w
clubbedTroll.fleeBattle(); // The troll shrieks in horror and runs away!
```
+Program output:
+
+```java
+The troll tries to grab you!
+The troll shrieks in horror and runs away!
+The troll tries to grab you! The troll swings at you with a club!
+The troll shrieks in horror and runs away!
+```
+
## Class diagram
+

## Applicability
-Use Decorator
-* To add responsibilities to individual objects dynamically and transparently, that is, without affecting other objects
-* For responsibilities that can be withdrawn
-* When extension by subclassing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class definition may be hidden or otherwise unavailable for subclassing
+Decorator is used to:
+
+* Add responsibilities to individual objects dynamically and transparently, that is, without
+affecting other objects.
+* For responsibilities that can be withdrawn.
+* When extension by subclassing is impractical. Sometimes a large number of independent extensions
+are possible and would produce an explosion of subclasses to support every combination. Or a class
+definition may be hidden or otherwise unavailable for subclassing.
## Tutorial
+
* [Decorator Pattern Tutorial](https://www.journaldev.com/1540/decorator-design-pattern-in-java-example)
## Real world examples
+
* [java.io.InputStream](http://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html), [java.io.OutputStream](http://docs.oracle.com/javase/8/docs/api/java/io/OutputStream.html),
[java.io.Reader](http://docs.oracle.com/javase/8/docs/api/java/io/Reader.html) and [java.io.Writer](http://docs.oracle.com/javase/8/docs/api/java/io/Writer.html)
* [java.util.Collections#synchronizedXXX()](http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#synchronizedCollection-java.util.Collection-)
diff --git a/decorator/pom.xml b/decorator/pom.xml
index c7e1a4d8d..b075704c8 100644
--- a/decorator/pom.xml
+++ b/decorator/pom.xml
@@ -29,7 +29,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
decorator
diff --git a/decorator/src/main/java/com/iluwatar/decorator/ClubbedTroll.java b/decorator/src/main/java/com/iluwatar/decorator/ClubbedTroll.java
index 70fd15489..74a1434e1 100644
--- a/decorator/src/main/java/com/iluwatar/decorator/ClubbedTroll.java
+++ b/decorator/src/main/java/com/iluwatar/decorator/ClubbedTroll.java
@@ -33,7 +33,7 @@ public class ClubbedTroll implements Troll {
private static final Logger LOGGER = LoggerFactory.getLogger(ClubbedTroll.class);
- private Troll decorated;
+ private final Troll decorated;
public ClubbedTroll(Troll decorated) {
this.decorated = decorated;
diff --git a/decorator/src/main/java/com/iluwatar/decorator/module-info.java b/decorator/src/main/java/com/iluwatar/decorator/module-info.java
deleted file mode 100644
index 50d17f022..000000000
--- a/decorator/src/main/java/com/iluwatar/decorator/module-info.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.
- */
-
-module com.iluwatar.decorator {
- requires org.slf4j;
-}
\ No newline at end of file
diff --git a/decorator/src/test/java/com/iluwatar/decorator/AppTest.java b/decorator/src/test/java/com/iluwatar/decorator/AppTest.java
index e8d4c8505..792d61233 100644
--- a/decorator/src/test/java/com/iluwatar/decorator/AppTest.java
+++ b/decorator/src/test/java/com/iluwatar/decorator/AppTest.java
@@ -25,13 +25,22 @@ package com.iluwatar.decorator;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Application test
*/
-public class AppTest {
+class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
+ * throws an exception.
+ */
@Test
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/decorator/src/test/java/com/iluwatar/decorator/SimpleTrollTest.java b/decorator/src/test/java/com/iluwatar/decorator/SimpleTrollTest.java
index c9f62407c..a398135e6 100644
--- a/decorator/src/test/java/com/iluwatar/decorator/SimpleTrollTest.java
+++ b/decorator/src/test/java/com/iluwatar/decorator/SimpleTrollTest.java
@@ -68,7 +68,7 @@ public class SimpleTrollTest {
private class InMemoryAppender extends AppenderBase {
- private List log = new LinkedList<>();
+ private final List log = new LinkedList<>();
public InMemoryAppender(Class clazz) {
((Logger) LoggerFactory.getLogger(clazz)).addAppender(this);
diff --git a/delegation/pom.xml b/delegation/pom.xml
index 63cd91842..d7ad81362 100644
--- a/delegation/pom.xml
+++ b/delegation/pom.xml
@@ -29,7 +29,7 @@
java-design-patterns
com.iluwatar
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
4.0.0
diff --git a/delegation/src/main/java/com/iluwatar/delegation/module-info.java b/delegation/src/main/java/com/iluwatar/delegation/module-info.java
deleted file mode 100644
index 156477cde..000000000
--- a/delegation/src/main/java/com/iluwatar/delegation/module-info.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.
- */
-
-module com.iluwatar.delegation {
- requires org.slf4j;
-}
\ No newline at end of file
diff --git a/delegation/src/test/java/com/iluwatar/delegation/simple/AppTest.java b/delegation/src/test/java/com/iluwatar/delegation/simple/AppTest.java
index 2865c76c1..8e20c9032 100644
--- a/delegation/src/test/java/com/iluwatar/delegation/simple/AppTest.java
+++ b/delegation/src/test/java/com/iluwatar/delegation/simple/AppTest.java
@@ -25,14 +25,23 @@ package com.iluwatar.delegation.simple;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Application Test Entry
*/
-public class AppTest {
+class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
+ * throws an exception.
+ */
@Test
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/delegation/src/test/java/com/iluwatar/delegation/simple/DelegateTest.java b/delegation/src/test/java/com/iluwatar/delegation/simple/DelegateTest.java
index 2da1e0571..8aefc4b56 100644
--- a/delegation/src/test/java/com/iluwatar/delegation/simple/DelegateTest.java
+++ b/delegation/src/test/java/com/iluwatar/delegation/simple/DelegateTest.java
@@ -86,7 +86,7 @@ public class DelegateTest {
*/
private class InMemoryAppender extends AppenderBase {
- private List log = new LinkedList<>();
+ private final List log = new LinkedList<>();
public InMemoryAppender() {
((Logger) LoggerFactory.getLogger("root")).addAppender(this);
diff --git a/dependency-injection/README.md b/dependency-injection/README.md
index abf647b50..e5bf93387 100644
--- a/dependency-injection/README.md
+++ b/dependency-injection/README.md
@@ -9,15 +9,19 @@ tags:
---
## Intent
-Dependency Injection is a software design pattern in which one or more dependencies (or services) are injected, or
-passed by reference, into a dependent object (or client) and are made part of the client's state. The pattern separates
-the creation of a client's dependencies from its own behavior, which allows program designs to be loosely coupled and
-to follow the inversion of control and single responsibility principles.
+
+Dependency Injection is a software design pattern in which one or more dependencies (or services)
+are injected, or passed by reference, into a dependent object (or client) and are made part of the
+client's state. The pattern separates the creation of a client's dependencies from its own behavior,
+which allows program designs to be loosely coupled and to follow the inversion of control and single
+responsibility principles.
## Explanation
+
Real world example
-> The old wizard likes to fill his pipe and smoke tobacco once in a while. However, he doesn't want to depend on a single tobacco brand only but likes to be able to enjoy them all interchangeably.
+> The old wizard likes to fill his pipe and smoke tobacco once in a while. However, he doesn't want
+> to depend on a single tobacco brand only but likes to be able to enjoy them all interchangeably.
In plain words
@@ -25,11 +29,12 @@ In plain words
Wikipedia says
-> In software engineering, dependency injection is a technique in which an object receives other objects that it depends on. These other objects are called dependencies.
+> In software engineering, dependency injection is a technique in which an object receives other
+> objects that it depends on. These other objects are called dependencies.
**Programmatic Example**
-Let's first introduce the tobacco brands.
+Let's first introduce the `Tobacco` interface and the concrete brands.
```java
public abstract class Tobacco {
@@ -52,7 +57,7 @@ public class OldTobyTobacco extends Tobacco {
}
```
-Next here's the wizard class hierarchy.
+Next here's the `Wizard` class hierarchy.
```java
public interface Wizard {
@@ -62,7 +67,7 @@ public interface Wizard {
public class AdvancedWizard implements Wizard {
- private Tobacco tobacco;
+ private final Tobacco tobacco;
public AdvancedWizard(Tobacco tobacco) {
this.tobacco = tobacco;
@@ -83,13 +88,15 @@ And lastly we can show how easy it is to give the old wizard any brand of tobacc
```
## Class diagram
+

## Applicability
-Use the Dependency Injection pattern when
-* When you need to remove knowledge of concrete implementation from object
-* To enable unit testing of classes in isolation using mock objects or stubs
+Use the Dependency Injection pattern when:
+
+* When you need to remove knowledge of concrete implementation from object.
+* To enable unit testing of classes in isolation using mock objects or stubs.
## Credits
diff --git a/dependency-injection/pom.xml b/dependency-injection/pom.xml
index 9baffe382..f3aaba520 100644
--- a/dependency-injection/pom.xml
+++ b/dependency-injection/pom.xml
@@ -29,7 +29,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
dependency-injection
diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/AdvancedWizard.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/AdvancedWizard.java
index e0c952186..f0ff2da94 100644
--- a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/AdvancedWizard.java
+++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/AdvancedWizard.java
@@ -29,7 +29,7 @@ package com.iluwatar.dependency.injection;
*/
public class AdvancedWizard implements Wizard {
- private Tobacco tobacco;
+ private final Tobacco tobacco;
public AdvancedWizard(Tobacco tobacco) {
this.tobacco = tobacco;
diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/GuiceWizard.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/GuiceWizard.java
index 319a635eb..d769ffd46 100644
--- a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/GuiceWizard.java
+++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/GuiceWizard.java
@@ -31,7 +31,7 @@ import javax.inject.Inject;
*/
public class GuiceWizard implements Wizard {
- private Tobacco tobacco;
+ private final Tobacco tobacco;
@Inject
public GuiceWizard(Tobacco tobacco) {
diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/SimpleWizard.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/SimpleWizard.java
index 40bca0ffb..0136ff69f 100644
--- a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/SimpleWizard.java
+++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/SimpleWizard.java
@@ -29,7 +29,7 @@ package com.iluwatar.dependency.injection;
*/
public class SimpleWizard implements Wizard {
- private OldTobyTobacco tobacco = new OldTobyTobacco();
+ private final OldTobyTobacco tobacco = new OldTobyTobacco();
public void smoke() {
tobacco.smoke(this);
diff --git a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/AppTest.java b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/AppTest.java
index 51115496d..52508814a 100644
--- a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/AppTest.java
+++ b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/AppTest.java
@@ -25,13 +25,22 @@ package com.iluwatar.dependency.injection;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Application test
*/
-public class AppTest {
+class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
+ * throws an exception.
+ */
@Test
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/utils/InMemoryAppender.java b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/utils/InMemoryAppender.java
index 9d0ad1b3b..d91099af9 100644
--- a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/utils/InMemoryAppender.java
+++ b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/utils/InMemoryAppender.java
@@ -37,7 +37,7 @@ import java.util.List;
*/
public class InMemoryAppender extends AppenderBase {
- private List log = new LinkedList<>();
+ private final List log = new LinkedList<>();
public InMemoryAppender(Class clazz) {
((Logger) LoggerFactory.getLogger(clazz)).addAppender(this);
diff --git a/dirty-flag/pom.xml b/dirty-flag/pom.xml
index c014cd41e..b796ab37a 100644
--- a/dirty-flag/pom.xml
+++ b/dirty-flag/pom.xml
@@ -29,11 +29,10 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
- com.iluwatar
dirty-flag
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
dirty-flag
http://maven.apache.org
@@ -45,6 +44,12 @@
junit-jupiter-engine
test
+
+ org.mockito
+ mockito-junit-jupiter
+ 3.5.0
+ test
+
diff --git a/dirty-flag/src/main/java/com/iluwatar/dirtyflag/World.java b/dirty-flag/src/main/java/com/iluwatar/dirtyflag/World.java
index db60924c1..1d4fbfa75 100644
--- a/dirty-flag/src/main/java/com/iluwatar/dirtyflag/World.java
+++ b/dirty-flag/src/main/java/com/iluwatar/dirtyflag/World.java
@@ -34,7 +34,7 @@ import java.util.List;
public class World {
private List countries;
- private DataFetcher df;
+ private final DataFetcher df;
public World() {
this.countries = new ArrayList();
diff --git a/dirty-flag/src/main/java/com/iluwatar/dirtyflag/module-info.java b/dirty-flag/src/main/java/com/iluwatar/dirtyflag/module-info.java
deleted file mode 100644
index bf47d2cd7..000000000
--- a/dirty-flag/src/main/java/com/iluwatar/dirtyflag/module-info.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.
- */
-
-module com.iluwatar.dirtyflag {
- requires org.slf4j;
-}
\ No newline at end of file
diff --git a/dirty-flag/src/test/java/org/dirty/flag/AppTest.java b/dirty-flag/src/test/java/org/dirty/flag/AppTest.java
index 1b604898b..82c7fea9b 100644
--- a/dirty-flag/src/test/java/org/dirty/flag/AppTest.java
+++ b/dirty-flag/src/test/java/org/dirty/flag/AppTest.java
@@ -26,12 +26,22 @@ package org.dirty.flag;
import com.iluwatar.dirtyflag.App;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Tests that Dirty-Flag example runs without errors.
*/
-public class AppTest {
+class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
+ * throws an exception.
+ */
+
@Test
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/dirty-flag/src/test/java/org/dirty/flag/DirtyFlagTest.java b/dirty-flag/src/test/java/org/dirty/flag/DirtyFlagTest.java
index 6a3274a45..9af9664d6 100644
--- a/dirty-flag/src/test/java/org/dirty/flag/DirtyFlagTest.java
+++ b/dirty-flag/src/test/java/org/dirty/flag/DirtyFlagTest.java
@@ -23,29 +23,27 @@
package org.dirty.flag;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
import com.iluwatar.dirtyflag.DataFetcher;
+import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
/**
* Application test
*/
-public class DirtyFlagTest {
+class DirtyFlagTest {
@Test
- public void testIsDirty() {
+ void testIsDirty() {
var df = new DataFetcher();
var countries = df.fetch();
- assertFalse(countries.isEmpty());
+ Assertions.assertFalse(countries.isEmpty());
}
@Test
- public void testIsNotDirty() {
+ void testIsNotDirty() {
var df = new DataFetcher();
df.fetch();
var countries = df.fetch();
- assertTrue(countries.isEmpty());
+ Assertions.assertTrue(countries.isEmpty());
}
}
diff --git a/double-buffer/pom.xml b/double-buffer/pom.xml
index 084cbc8c9..cc4032074 100644
--- a/double-buffer/pom.xml
+++ b/double-buffer/pom.xml
@@ -29,7 +29,7 @@
java-design-patterns
com.iluwatar
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
4.0.0
@@ -44,6 +44,11 @@
org.apache.commons
commons-lang3
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
diff --git a/double-buffer/src/main/java/com/iluwatar/doublebuffer/FrameBuffer.java b/double-buffer/src/main/java/com/iluwatar/doublebuffer/FrameBuffer.java
index 5f683cf1e..4b974a2e8 100644
--- a/double-buffer/src/main/java/com/iluwatar/doublebuffer/FrameBuffer.java
+++ b/double-buffer/src/main/java/com/iluwatar/doublebuffer/FrameBuffer.java
@@ -33,7 +33,7 @@ public class FrameBuffer implements Buffer {
public static final int WIDTH = 10;
public static final int HEIGHT = 8;
- private Pixel[] pixels = new Pixel[WIDTH * HEIGHT];
+ private final Pixel[] pixels = new Pixel[WIDTH * HEIGHT];
public FrameBuffer() {
clearAll();
diff --git a/double-buffer/src/main/java/com/iluwatar/doublebuffer/Pixel.java b/double-buffer/src/main/java/com/iluwatar/doublebuffer/Pixel.java
index 501797743..54f130b1d 100644
--- a/double-buffer/src/main/java/com/iluwatar/doublebuffer/Pixel.java
+++ b/double-buffer/src/main/java/com/iluwatar/doublebuffer/Pixel.java
@@ -31,7 +31,7 @@ public enum Pixel {
WHITE(0),
BLACK(1);
- private int color;
+ private final int color;
Pixel(int color) {
this.color = color;
diff --git a/double-buffer/src/main/java/com/iluwatar/doublebuffer/Scene.java b/double-buffer/src/main/java/com/iluwatar/doublebuffer/Scene.java
index 2c1503918..8ee72ded4 100644
--- a/double-buffer/src/main/java/com/iluwatar/doublebuffer/Scene.java
+++ b/double-buffer/src/main/java/com/iluwatar/doublebuffer/Scene.java
@@ -35,7 +35,7 @@ public class Scene {
private static final Logger LOGGER = LoggerFactory.getLogger(Scene.class);
- private Buffer[] frameBuffers;
+ private final Buffer[] frameBuffers;
private int current;
diff --git a/double-buffer/src/test/java/com/iluwatar/doublebuffer/AppTest.java b/double-buffer/src/test/java/com/iluwatar/doublebuffer/AppTest.java
index eb89a4044..6612d2b00 100644
--- a/double-buffer/src/test/java/com/iluwatar/doublebuffer/AppTest.java
+++ b/double-buffer/src/test/java/com/iluwatar/doublebuffer/AppTest.java
@@ -25,14 +25,23 @@ package com.iluwatar.doublebuffer;
import org.junit.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* App unit test.
*/
public class AppTest {
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
+ * throws an exception.
+ */
+
@Test
- public void testMain() {
- App.main(new String[]{});
+ public void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/double-checked-locking/pom.xml b/double-checked-locking/pom.xml
index a77546386..04aba2260 100644
--- a/double-checked-locking/pom.xml
+++ b/double-checked-locking/pom.xml
@@ -27,7 +27,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
double-checked-locking
diff --git a/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/module-info.java b/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/module-info.java
deleted file mode 100644
index 4f4216ea7..000000000
--- a/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/module-info.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.
- */
-
-module com.iluwatar.doublecheckedlocking {
- requires org.slf4j;
-}
\ No newline at end of file
diff --git a/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/AppTest.java b/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/AppTest.java
index 6eac88fcd..e24e51094 100644
--- a/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/AppTest.java
+++ b/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/AppTest.java
@@ -25,13 +25,23 @@ package com.iluwatar.doublechecked.locking;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Application test
*/
-public class AppTest {
+class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
+ * throws an exception.
+ */
+
@Test
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/InventoryTest.java b/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/InventoryTest.java
index e8ea7c6f8..fe0cbf5e9 100644
--- a/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/InventoryTest.java
+++ b/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/InventoryTest.java
@@ -109,7 +109,7 @@ public class InventoryTest {
private class InMemoryAppender extends AppenderBase {
- private List log = new LinkedList<>();
+ private final List log = new LinkedList<>();
public InMemoryAppender(Class clazz) {
((Logger) LoggerFactory.getLogger(clazz)).addAppender(this);
diff --git a/double-dispatch/pom.xml b/double-dispatch/pom.xml
index 9582797a2..275b505a5 100644
--- a/double-dispatch/pom.xml
+++ b/double-dispatch/pom.xml
@@ -29,7 +29,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
double-dispatch
diff --git a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Rectangle.java b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Rectangle.java
index bd832287c..ea18ca3dc 100644
--- a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Rectangle.java
+++ b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Rectangle.java
@@ -28,10 +28,10 @@ package com.iluwatar.doubledispatch;
*/
public class Rectangle {
- private int left;
- private int top;
- private int right;
- private int bottom;
+ private final int left;
+ private final int top;
+ private final int right;
+ private final int bottom;
/**
* Constructor.
diff --git a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationMir.java b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationMir.java
index cc61edcdc..5b6535752 100644
--- a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationMir.java
+++ b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationMir.java
@@ -45,7 +45,7 @@ public class SpaceStationMir extends GameObject {
@Override
public void collisionResolve(FlamingAsteroid asteroid) {
- LOGGER.info(AppConstants.HITS, " {} is damaged! {} is set on fire!", asteroid.getClass()
+ LOGGER.info(AppConstants.HITS + " {} is damaged! {} is set on fire!", asteroid.getClass()
.getSimpleName(),
this.getClass().getSimpleName(), this.getClass().getSimpleName(), this.getClass()
.getSimpleName());
@@ -55,14 +55,14 @@ public class SpaceStationMir extends GameObject {
@Override
public void collisionResolve(Meteoroid meteoroid) {
- LOGGER.info(AppConstants.HITS, " {} is damaged!", meteoroid.getClass().getSimpleName(),
+ LOGGER.info(AppConstants.HITS + " {} is damaged!", meteoroid.getClass().getSimpleName(),
this.getClass().getSimpleName(), this.getClass().getSimpleName());
setDamaged(true);
}
@Override
public void collisionResolve(SpaceStationMir mir) {
- LOGGER.info(AppConstants.HITS, " {} is damaged!", mir.getClass().getSimpleName(),
+ LOGGER.info(AppConstants.HITS + " {} is damaged!", mir.getClass().getSimpleName(),
this.getClass().getSimpleName(), this.getClass().getSimpleName());
setDamaged(true);
}
diff --git a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/module-info.java b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/module-info.java
deleted file mode 100644
index b1bc2e824..000000000
--- a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/module-info.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.
- */
-
-module com.iluwatar.doubledispatch {
- requires org.slf4j;
-}
\ No newline at end of file
diff --git a/double-dispatch/src/test/java/com/iluwatar/doubledispatch/AppTest.java b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/AppTest.java
index 67ca00c56..e5df7a2be 100644
--- a/double-dispatch/src/test/java/com/iluwatar/doubledispatch/AppTest.java
+++ b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/AppTest.java
@@ -25,13 +25,23 @@ package com.iluwatar.doubledispatch;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Application test
*/
-public class AppTest {
+class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
+ * throws an exception.
+ */
+
@Test
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/eip-aggregator/pom.xml b/eip-aggregator/pom.xml
index 578d1bbf2..efee153a3 100644
--- a/eip-aggregator/pom.xml
+++ b/eip-aggregator/pom.xml
@@ -31,7 +31,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
diff --git a/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/AppTest.java b/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/AppTest.java
index ed604e8c2..3da3b3e66 100644
--- a/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/AppTest.java
+++ b/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/AppTest.java
@@ -25,13 +25,22 @@ package com.iluwatar.eip.aggregator;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Test for App class
*/
-public class AppTest {
+class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
+ * throws an exception.
+ */
@Test
- public void testMain() throws Exception {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/eip-message-channel/pom.xml b/eip-message-channel/pom.xml
index bea72b1f9..12fe153e3 100644
--- a/eip-message-channel/pom.xml
+++ b/eip-message-channel/pom.xml
@@ -30,7 +30,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
eip-message-channel
diff --git a/eip-message-channel/src/main/java/com/iluwatar/eip/message/channel/module-info.java b/eip-message-channel/src/main/java/com/iluwatar/eip/message/channel/module-info.java
deleted file mode 100644
index b904ee1c8..000000000
--- a/eip-message-channel/src/main/java/com/iluwatar/eip/message/channel/module-info.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.
- */
-
-module com.iluwatar.eipmessagechannel {
- requires org.slf4j;
- requires camel.core;
-}
\ No newline at end of file
diff --git a/eip-message-channel/src/test/java/com/iluwatar/eip/message/channel/AppTest.java b/eip-message-channel/src/test/java/com/iluwatar/eip/message/channel/AppTest.java
index 9f11c0209..14cdc5c65 100644
--- a/eip-message-channel/src/test/java/com/iluwatar/eip/message/channel/AppTest.java
+++ b/eip-message-channel/src/test/java/com/iluwatar/eip/message/channel/AppTest.java
@@ -25,13 +25,22 @@ package com.iluwatar.eip.message.channel;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Application test
*/
-public class AppTest {
+class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
+ * throws an exception.
+ */
@Test
- public void test() throws Exception {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/eip-publish-subscribe/pom.xml b/eip-publish-subscribe/pom.xml
index e7b5462b6..f354b1ee3 100644
--- a/eip-publish-subscribe/pom.xml
+++ b/eip-publish-subscribe/pom.xml
@@ -28,7 +28,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
eip-publish-subscribe
diff --git a/eip-publish-subscribe/src/main/java/com/iluwatar/eip/publish/subscribe/module-info.java b/eip-publish-subscribe/src/main/java/com/iluwatar/eip/publish/subscribe/module-info.java
deleted file mode 100644
index 50eab8360..000000000
--- a/eip-publish-subscribe/src/main/java/com/iluwatar/eip/publish/subscribe/module-info.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.
- */
-
-module com.iluwatar.eippublishsubscribe {
- requires org.slf4j;
- requires camel.core;
-}
\ No newline at end of file
diff --git a/eip-publish-subscribe/src/test/java/com/iluwatar/eip/publish/subscribe/AppTest.java b/eip-publish-subscribe/src/test/java/com/iluwatar/eip/publish/subscribe/AppTest.java
index 107e954ed..f910d0abe 100644
--- a/eip-publish-subscribe/src/test/java/com/iluwatar/eip/publish/subscribe/AppTest.java
+++ b/eip-publish-subscribe/src/test/java/com/iluwatar/eip/publish/subscribe/AppTest.java
@@ -25,13 +25,22 @@ package com.iluwatar.eip.publish.subscribe;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Application test
*/
-public class AppTest {
+class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
+ * throws an exception.
+ */
@Test
- public void test() throws Exception {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/eip-splitter/pom.xml b/eip-splitter/pom.xml
index 9c06f3f8d..5b4758a9e 100644
--- a/eip-splitter/pom.xml
+++ b/eip-splitter/pom.xml
@@ -31,7 +31,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
diff --git a/eip-splitter/src/test/java/com/iluwatar/eip/splitter/AppTest.java b/eip-splitter/src/test/java/com/iluwatar/eip/splitter/AppTest.java
index 1a7dfcb0a..d5936282e 100644
--- a/eip-splitter/src/test/java/com/iluwatar/eip/splitter/AppTest.java
+++ b/eip-splitter/src/test/java/com/iluwatar/eip/splitter/AppTest.java
@@ -25,13 +25,22 @@ package com.iluwatar.eip.splitter;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Test for App class
*/
-public class AppTest {
+class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
+ * throws an exception.
+ */
@Test
- public void testMain() throws Exception {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/eip-wire-tap/pom.xml b/eip-wire-tap/pom.xml
index 06cbc33db..332861547 100644
--- a/eip-wire-tap/pom.xml
+++ b/eip-wire-tap/pom.xml
@@ -31,7 +31,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
diff --git a/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java b/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java
index 31154043c..8be8bcbe7 100644
--- a/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java
+++ b/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java
@@ -25,13 +25,22 @@ package com.iluwatar.eip.wiretap;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Test for App class
*/
-public class AppTest {
+class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
+ * throws an exception.
+ */
@Test
- public void testMain() throws Exception {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/event-aggregator/pom.xml b/event-aggregator/pom.xml
index 5553de2e3..813521c48 100644
--- a/event-aggregator/pom.xml
+++ b/event-aggregator/pom.xml
@@ -28,7 +28,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
event-aggregator
diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Event.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Event.java
index 7a125c042..91bb020ee 100644
--- a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Event.java
+++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Event.java
@@ -31,7 +31,7 @@ public enum Event {
STARK_SIGHTED("Stark sighted"), WARSHIPS_APPROACHING("Warships approaching"), TRAITOR_DETECTED(
"Traitor detected");
- private String description;
+ private final String description;
Event(String description) {
this.description = description;
diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/EventEmitter.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/EventEmitter.java
index 9985cee60..7d3f32a68 100644
--- a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/EventEmitter.java
+++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/EventEmitter.java
@@ -31,7 +31,7 @@ import java.util.List;
*/
public abstract class EventEmitter {
- private List observers;
+ private final List observers;
public EventEmitter() {
observers = new LinkedList<>();
diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Weekday.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Weekday.java
index 9ec61339c..1e0ce9491 100644
--- a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Weekday.java
+++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Weekday.java
@@ -36,7 +36,7 @@ public enum Weekday {
SATURDAY("Saturday"),
SUNDAY("Sunday");
- private String description;
+ private final String description;
Weekday(String description) {
this.description = description;
diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/module-info.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/module-info.java
deleted file mode 100644
index 93ebd3173..000000000
--- a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/module-info.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.
- */
-
-module com.iluwatar.eventaggregator {
- requires org.slf4j;
-}
\ No newline at end of file
diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/AppTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/AppTest.java
index a65424023..e56bc9d7f 100644
--- a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/AppTest.java
+++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/AppTest.java
@@ -25,13 +25,22 @@ package com.iluwatar.event.aggregator;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Application test
*/
-public class AppTest {
+class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
+ * throws an exception.
+ */
@Test
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingJoffreyTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingJoffreyTest.java
index a8bb6cbaa..f8aa5cb37 100644
--- a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingJoffreyTest.java
+++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingJoffreyTest.java
@@ -74,7 +74,7 @@ public class KingJoffreyTest {
}
private class InMemoryAppender extends AppenderBase {
- private List log = new LinkedList<>();
+ private final List log = new LinkedList<>();
public InMemoryAppender(Class> clazz) {
((Logger) LoggerFactory.getLogger(clazz)).addAppender(this);
diff --git a/event-asynchronous/pom.xml b/event-asynchronous/pom.xml
index 001b3b9a8..06921a100 100644
--- a/event-asynchronous/pom.xml
+++ b/event-asynchronous/pom.xml
@@ -29,7 +29,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
event-asynchronous
diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java
index 6925a2ffd..68c4c9781 100644
--- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java
+++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java
@@ -33,9 +33,9 @@ public class Event implements IEvent, Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(Event.class);
- private int eventId;
- private int eventTime;
- private boolean isSynchronous;
+ private final int eventId;
+ private final int eventTime;
+ private final boolean isSynchronous;
private Thread thread;
private boolean isComplete = false;
private ThreadCompleteListener eventListener;
diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java
index 14d28860b..55671fd82 100644
--- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java
+++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java
@@ -43,8 +43,8 @@ public class EventManager implements ThreadCompleteListener {
public static final int MAX_ID = MAX_RUNNING_EVENTS;
public static final int MAX_EVENT_TIME = 1800; // in seconds / 30 minutes.
private int currentlyRunningSyncEvent = -1;
- private Random rand;
- private Map eventPool;
+ private final Random rand;
+ private final Map eventPool;
private static final String DOES_NOT_EXIST = " does not exist.";
diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/module-info.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/module-info.java
deleted file mode 100644
index aa9b6c29d..000000000
--- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/module-info.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.
- */
-
-module com.iluwatar.eventasynchronous {
- requires org.slf4j;
-}
\ No newline at end of file
diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java
index 76554d6b1..638e77f87 100644
--- a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java
+++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java
@@ -25,12 +25,22 @@ package com.iluwatar.event.asynchronous;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Tests that EventAsynchronous example runs without errors.
*/
-public class AppTest {
+class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
+ * throws an exception.
+ */
+
@Test
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/event-driven-architecture/pom.xml b/event-driven-architecture/pom.xml
index 17d2795c4..c40a8821b 100644
--- a/event-driven-architecture/pom.xml
+++ b/event-driven-architecture/pom.xml
@@ -31,7 +31,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
event-driven-architecture
diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/event/UserCreatedEvent.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/event/UserCreatedEvent.java
index c18426c95..dd5e65a9a 100644
--- a/event-driven-architecture/src/main/java/com/iluwatar/eda/event/UserCreatedEvent.java
+++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/event/UserCreatedEvent.java
@@ -32,7 +32,7 @@ import com.iluwatar.eda.model.User;
*/
public class UserCreatedEvent extends AbstractEvent {
- private User user;
+ private final User user;
public UserCreatedEvent(User user) {
this.user = user;
diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/event/UserUpdatedEvent.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/event/UserUpdatedEvent.java
index 59583053c..05370c6a6 100644
--- a/event-driven-architecture/src/main/java/com/iluwatar/eda/event/UserUpdatedEvent.java
+++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/event/UserUpdatedEvent.java
@@ -32,7 +32,7 @@ import com.iluwatar.eda.model.User;
*/
public class UserUpdatedEvent extends AbstractEvent {
- private User user;
+ private final User user;
public UserUpdatedEvent(User user) {
this.user = user;
diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/EventDispatcher.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/EventDispatcher.java
index dd72c1e93..74a7ee145 100644
--- a/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/EventDispatcher.java
+++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/EventDispatcher.java
@@ -32,7 +32,7 @@ import java.util.Map;
*/
public class EventDispatcher {
- private Map, Handler extends Event>> handlers;
+ private final Map, Handler extends Event>> handlers;
public EventDispatcher() {
handlers = new HashMap<>();
diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/model/User.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/model/User.java
index 1492c175c..0c9f12501 100644
--- a/event-driven-architecture/src/main/java/com/iluwatar/eda/model/User.java
+++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/model/User.java
@@ -32,7 +32,7 @@ import com.iluwatar.eda.event.UserUpdatedEvent;
*/
public class User {
- private String username;
+ private final String username;
public User(String username) {
this.username = username;
diff --git a/event-driven-architecture/src/test/java/com/iluwatar/eda/AppTest.java b/event-driven-architecture/src/test/java/com/iluwatar/eda/AppTest.java
index 0f1720363..eb944a22c 100644
--- a/event-driven-architecture/src/test/java/com/iluwatar/eda/AppTest.java
+++ b/event-driven-architecture/src/test/java/com/iluwatar/eda/AppTest.java
@@ -25,12 +25,22 @@ package com.iluwatar.eda;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Tests that Event Driven Architecture example runs without errors.
*/
-public class AppTest {
+class AppTest {
+
+ /**
+ * Issue: Add at least one assertion to this test case.
+ *
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
+ * throws an exception.
+ */
+
@Test
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/event-queue/pom.xml b/event-queue/pom.xml
index fd8ce9902..232b9abaa 100644
--- a/event-queue/pom.xml
+++ b/event-queue/pom.xml
@@ -30,7 +30,7 @@
java-design-patterns
com.iluwatar
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
event-queue
diff --git a/event-queue/src/main/java/com/iluwatar/event/queue/Audio.java b/event-queue/src/main/java/com/iluwatar/event/queue/Audio.java
index 4286a5ed0..a0ff5d987 100644
--- a/event-queue/src/main/java/com/iluwatar/event/queue/Audio.java
+++ b/event-queue/src/main/java/com/iluwatar/event/queue/Audio.java
@@ -49,7 +49,7 @@ public class Audio {
private volatile Thread updateThread = null;
- private PlayMessage[] pendingAudio = new PlayMessage[MAX_PENDING];
+ private final PlayMessage[] pendingAudio = new PlayMessage[MAX_PENDING];
// Visible only for testing purposes
Audio() {
diff --git a/event-sourcing/README.md b/event-sourcing/README.md
index 6d24a40e5..06b7ec0c2 100644
--- a/event-sourcing/README.md
+++ b/event-sourcing/README.md
@@ -24,11 +24,11 @@ Use the Event Sourcing pattern when
## Real world examples
-* [The Lmax Architecture] (https://martinfowler.com/articles/lmax.html)
+* [The Lmax Architecture](https://martinfowler.com/articles/lmax.html)
## Credits
-* [Martin Fowler - Event Sourcing] (https://martinfowler.com/eaaDev/EventSourcing.html)
-* [Event Sourcing | Microsoft Docs] (https://docs.microsoft.com/en-us/azure/architecture/patterns/event-sourcing)
-* [Reference 3: Introducing Event Sourcing] (https://msdn.microsoft.com/en-us/library/jj591559.aspx)
+* [Martin Fowler - Event Sourcing](https://martinfowler.com/eaaDev/EventSourcing.html)
+* [Event Sourcing in Microsoft's documentation](https://docs.microsoft.com/en-us/azure/architecture/patterns/event-sourcing)
+* [Reference 3: Introducing Event Sourcing](https://msdn.microsoft.com/en-us/library/jj591559.aspx)
* [Event Sourcing pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/event-sourcing)
diff --git a/event-sourcing/pom.xml b/event-sourcing/pom.xml
index 0d1592abb..44af5fc5e 100644
--- a/event-sourcing/pom.xml
+++ b/event-sourcing/pom.xml
@@ -30,7 +30,7 @@
java-design-patterns
com.iluwatar
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
event-sourcing
diff --git a/execute-around/README.md b/execute-around/README.md
index 2873aef9b..600e41196 100644
--- a/execute-around/README.md
+++ b/execute-around/README.md
@@ -9,15 +9,18 @@ tags:
---
## Intent
-Execute Around idiom frees the user from certain actions that should always be executed before and after the business
-method. A good example of this is resource allocation and deallocation leaving the user to specify only what to do with
-the resource.
+
+Execute Around idiom frees the user from certain actions that should always be executed before and
+after the business method. A good example of this is resource allocation and deallocation leaving
+the user to specify only what to do with the resource.
## Explanation
Real world example
-> We need to provide a class that can be used to write text strings to files. To make it easy for the user we let our service class open and close the file automatically, the user only has to specify what is written into which file.
+> We need to provide a class that can be used to write text strings to files. To make it easy for
+> the user we let our service class open and close the file automatically, the user only has to
+> specify what is written into which file.
In plain words
@@ -25,7 +28,9 @@ In plain words
[Stack Overflow](https://stackoverflow.com/questions/341971/what-is-the-execute-around-idiom) says
-> Basically it's the pattern where you write a method to do things which are always required, e.g. resource allocation and clean-up, and make the caller pass in "what we want to do with the resource".
+> Basically it's the pattern where you write a method to do things which are always required, e.g.
+> resource allocation and clean-up, and make the caller pass in "what we want to do with the
+> resource".
**Programmatic Example**
@@ -61,12 +66,15 @@ To utilize the file writer the following code is needed.
```
## Class diagram
+

## Applicability
+
Use the Execute Around idiom when
-* you use an API that requires methods to be called in pairs such as open/close or allocate/deallocate.
+* You use an API that requires methods to be called in pairs such as open/close or
+allocate/deallocate.
## Credits
diff --git a/execute-around/pom.xml b/execute-around/pom.xml
index 1752f04f5..2d0640fe0 100644
--- a/execute-around/pom.xml
+++ b/execute-around/pom.xml
@@ -29,7 +29,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
execute-around
diff --git a/execute-around/src/main/java/com/iluwatar/execute/around/module-info.java b/execute-around/src/main/java/com/iluwatar/execute/around/module-info.java
deleted file mode 100644
index a3e179094..000000000
--- a/execute-around/src/main/java/com/iluwatar/execute/around/module-info.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.
- */
-
-module com.iluwatar.executearound {
- requires org.slf4j;
-}
\ No newline at end of file
diff --git a/execute-around/src/test/java/com/iluwatar/execute/around/AppTest.java b/execute-around/src/test/java/com/iluwatar/execute/around/AppTest.java
index 5512ba8d2..6516ede4a 100644
--- a/execute-around/src/test/java/com/iluwatar/execute/around/AppTest.java
+++ b/execute-around/src/test/java/com/iluwatar/execute/around/AppTest.java
@@ -29,19 +29,21 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Tests execute-around example.
*/
-public class AppTest {
+class AppTest {
@Test
- public void test() throws IOException {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
@BeforeEach
@AfterEach
- public void cleanup() {
+ void cleanup() {
var file = new File("testfile.txt");
file.delete();
}
diff --git a/extension-objects/pom.xml b/extension-objects/pom.xml
index 0194357ed..7247a9676 100644
--- a/extension-objects/pom.xml
+++ b/extension-objects/pom.xml
@@ -29,7 +29,7 @@
java-design-patterns
com.iluwatar
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
4.0.0
diff --git a/extension-objects/src/main/java/concreteextensions/Commander.java b/extension-objects/src/main/java/concreteextensions/Commander.java
index 5a0552b20..ffd46dd25 100644
--- a/extension-objects/src/main/java/concreteextensions/Commander.java
+++ b/extension-objects/src/main/java/concreteextensions/Commander.java
@@ -35,7 +35,7 @@ public class Commander implements CommanderExtension {
private static final Logger LOGGER = LoggerFactory.getLogger(Commander.class);
- private CommanderUnit unit;
+ private final CommanderUnit unit;
public Commander(CommanderUnit commanderUnit) {
this.unit = commanderUnit;
@@ -45,4 +45,8 @@ public class Commander implements CommanderExtension {
public void commanderReady() {
LOGGER.info("[Commander] " + unit.getName() + " is ready!");
}
+
+ public CommanderUnit getUnit() {
+ return unit;
+ }
}
diff --git a/extension-objects/src/main/java/concreteextensions/Sergeant.java b/extension-objects/src/main/java/concreteextensions/Sergeant.java
index a45b82f11..aea2ddaca 100644
--- a/extension-objects/src/main/java/concreteextensions/Sergeant.java
+++ b/extension-objects/src/main/java/concreteextensions/Sergeant.java
@@ -35,7 +35,7 @@ public class Sergeant implements SergeantExtension {
private static final Logger LOGGER = LoggerFactory.getLogger(Sergeant.class);
- private SergeantUnit unit;
+ private final SergeantUnit unit;
public Sergeant(SergeantUnit sergeantUnit) {
this.unit = sergeantUnit;
@@ -43,6 +43,10 @@ public class Sergeant implements SergeantExtension {
@Override
public void sergeantReady() {
- LOGGER.info("[Sergeant] " + unit.getName() + " is ready! ");
+ LOGGER.info("[Sergeant] " + unit.getName() + " is ready!");
+ }
+
+ public SergeantUnit getUnit() {
+ return unit;
}
}
diff --git a/extension-objects/src/main/java/concreteextensions/Soldier.java b/extension-objects/src/main/java/concreteextensions/Soldier.java
index b47ba595d..3ceaa7880 100644
--- a/extension-objects/src/main/java/concreteextensions/Soldier.java
+++ b/extension-objects/src/main/java/concreteextensions/Soldier.java
@@ -34,7 +34,7 @@ import units.SoldierUnit;
public class Soldier implements SoldierExtension {
private static final Logger LOGGER = LoggerFactory.getLogger(Soldier.class);
- private SoldierUnit unit;
+ private final SoldierUnit unit;
public Soldier(SoldierUnit soldierUnit) {
this.unit = soldierUnit;
@@ -42,6 +42,10 @@ public class Soldier implements SoldierExtension {
@Override
public void soldierReady() {
- LOGGER.info("[Solider] " + unit.getName() + " is ready!");
+ LOGGER.info("[Soldier] " + unit.getName() + " is ready!");
+ }
+
+ public SoldierUnit getUnit() {
+ return unit;
}
}
diff --git a/extension-objects/src/test/java/AppTest.java b/extension-objects/src/test/java/AppTest.java
index 2af33e506..321bf758f 100644
--- a/extension-objects/src/test/java/AppTest.java
+++ b/extension-objects/src/test/java/AppTest.java
@@ -23,13 +23,16 @@
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Created by Srdjan on 03-May-17.
*/
-public class AppTest {
+class AppTest {
+
@Test
- public void main() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
\ No newline at end of file
diff --git a/extension-objects/src/test/java/concreteextensions/CommanderTest.java b/extension-objects/src/test/java/concreteextensions/CommanderTest.java
index 60ff614e4..0c9d00baf 100644
--- a/extension-objects/src/test/java/concreteextensions/CommanderTest.java
+++ b/extension-objects/src/test/java/concreteextensions/CommanderTest.java
@@ -23,17 +23,43 @@
package concreteextensions;
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.read.ListAppender;
import org.junit.jupiter.api.Test;
+import org.slf4j.LoggerFactory;
import units.CommanderUnit;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
/**
* Created by Srdjan on 03-May-17.
+ *
+ * Modified by ToxicDreamz on 15-Aug-20
*/
-public class CommanderTest {
+class CommanderTest {
+
@Test
- public void commanderReady() {
+ void shouldExecuteCommanderReady() {
+
+ Logger commanderLogger = (Logger) LoggerFactory.getLogger(Commander.class);
+
+ ListAppender listAppender = new ListAppender<>();
+ listAppender.start();
+
+ commanderLogger.addAppender(listAppender);
+
final var commander = new Commander(new CommanderUnit("CommanderUnitTest"));
commander.commanderReady();
+
+ List logsList = listAppender.list;
+ assertEquals("[Commander] " + commander.getUnit().getName() + " is ready!", logsList.get(0)
+ .getMessage());
+ assertEquals(Level.INFO, logsList.get(0)
+ .getLevel());
}
}
\ No newline at end of file
diff --git a/extension-objects/src/test/java/concreteextensions/SergeantTest.java b/extension-objects/src/test/java/concreteextensions/SergeantTest.java
index a5a60d914..3970a5c80 100644
--- a/extension-objects/src/test/java/concreteextensions/SergeantTest.java
+++ b/extension-objects/src/test/java/concreteextensions/SergeantTest.java
@@ -23,17 +23,41 @@
package concreteextensions;
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.read.ListAppender;
import org.junit.jupiter.api.Test;
+import org.slf4j.LoggerFactory;
import units.SergeantUnit;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
/**
* Created by Srdjan on 03-May-17.
*/
-public class SergeantTest {
+class SergeantTest {
+
@Test
- public void sergeantReady() {
+ void sergeantReady() {
+
+ Logger sergeantLogger = (Logger) LoggerFactory.getLogger(Sergeant.class);
+
+ ListAppender listAppender = new ListAppender<>();
+ listAppender.start();
+
+ sergeantLogger.addAppender(listAppender);
+
final var sergeant = new Sergeant(new SergeantUnit("SergeantUnitTest"));
sergeant.sergeantReady();
+
+ List logsList = listAppender.list;
+ assertEquals("[Sergeant] " + sergeant.getUnit().getName() + " is ready!", logsList.get(0)
+ .getMessage());
+ assertEquals(Level.INFO, logsList.get(0)
+ .getLevel());
}
}
\ No newline at end of file
diff --git a/extension-objects/src/test/java/concreteextensions/SoldierTest.java b/extension-objects/src/test/java/concreteextensions/SoldierTest.java
index 89c8c2d91..98224e848 100644
--- a/extension-objects/src/test/java/concreteextensions/SoldierTest.java
+++ b/extension-objects/src/test/java/concreteextensions/SoldierTest.java
@@ -23,17 +23,41 @@
package concreteextensions;
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.read.ListAppender;
import org.junit.jupiter.api.Test;
+import org.slf4j.LoggerFactory;
import units.SoldierUnit;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
/**
* Created by Srdjan on 03-May-17.
*/
-public class SoldierTest {
+class SoldierTest {
+
@Test
- public void soldierReady() {
+ void soldierReady() {
+
+ Logger soldierLogger = (Logger) LoggerFactory.getLogger(Soldier.class);
+
+ ListAppender listAppender = new ListAppender<>();
+ listAppender.start();
+
+ soldierLogger.addAppender(listAppender);
+
final var soldier = new Soldier(new SoldierUnit("SoldierUnitTest"));
soldier.soldierReady();
+
+ List logsList = listAppender.list;
+ assertEquals("[Soldier] " + soldier.getUnit().getName() + " is ready!", logsList.get(0)
+ .getMessage());
+ assertEquals(Level.INFO, logsList.get(0)
+ .getLevel());
}
}
\ No newline at end of file
diff --git a/facade/README.md b/facade/README.md
index 018c493a7..f6765e325 100644
--- a/facade/README.md
+++ b/facade/README.md
@@ -10,14 +10,18 @@ tags:
---
## Intent
-Provide a unified interface to a set of interfaces in a subsystem.
-Facade defines a higher-level interface that makes the subsystem easier to use.
+
+Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level
+interface that makes the subsystem easier to use.
## Explanation
Real world example
-> How does a goldmine work? "Well, the miners go down there and dig gold!" you say. That is what you believe because you are using a simple interface that goldmine provides on the outside, internally it has to do a lot of stuff to make it happen. This simple interface to the complex subsystem is a facade.
+> How does a goldmine work? "Well, the miners go down there and dig gold!" you say. That is what you
+> believe because you are using a simple interface that goldmine provides on the outside, internally
+> it has to do a lot of stuff to make it happen. This simple interface to the complex subsystem is a
+> facade.
In plain words
@@ -25,11 +29,13 @@ In plain words
Wikipedia says
-> A facade is an object that provides a simplified interface to a larger body of code, such as a class library.
+> A facade is an object that provides a simplified interface to a larger body of code, such as a
+> class library.
**Programmatic Example**
-Taking our goldmine example from above. Here we have the dwarven mine worker hierarchy
+Let's take our goldmine example from above. Here we have the dwarven mine worker hierarchy. First
+there's a base class `DwarvenMineWorker`:
```java
public abstract class DwarvenMineWorker {
@@ -83,11 +89,16 @@ public abstract class DwarvenMineWorker {
public abstract String name();
- static enum Action {
+ enum Action {
GO_TO_SLEEP, WAKE_UP, GO_HOME, GO_TO_MINE, WORK
}
}
+```
+Then we have the concrete dwarf classes `DwarvenTunnelDigger`, `DwarvenGoldDigger` and
+`DwarvenCartOperator`:
+
+```java
public class DwarvenTunnelDigger extends DwarvenMineWorker {
private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenTunnelDigger.class);
@@ -135,7 +146,7 @@ public class DwarvenCartOperator extends DwarvenMineWorker {
```
-To operate all these goldmine workers we have the facade
+To operate all these goldmine workers we have the `DwarvenGoldmineFacade`:
```java
public class DwarvenGoldmineFacade {
@@ -168,22 +179,27 @@ public class DwarvenGoldmineFacade {
}
```
-Now to use the facade
+Now let's use the facade:
```java
-DwarvenGoldmineFacade facade = new DwarvenGoldmineFacade();
+var facade = new DwarvenGoldmineFacade();
facade.startNewDay();
+facade.digOutGold();
+facade.endDay();
+```
+
+Program output:
+
+```java
// Dwarf gold digger wakes up.
// Dwarf gold digger goes to the mine.
// Dwarf cart operator wakes up.
// Dwarf cart operator goes to the mine.
// Dwarven tunnel digger wakes up.
// Dwarven tunnel digger goes to the mine.
-facade.digOutGold();
// Dwarf gold digger digs for gold.
// Dwarf cart operator moves gold chunks out of the mine.
// Dwarven tunnel digger creates another promising tunnel.
-facade.endDay();
// Dwarf gold digger goes home.
// Dwarf gold digger goes to sleep.
// Dwarf cart operator goes home.
@@ -193,14 +209,25 @@ facade.endDay();
```
## Class diagram
+

## Applicability
+
Use the Facade pattern when
-* you want to provide a simple interface to a complex subsystem. Subsystems often get more complex as they evolve. Most patterns, when applied, result in more and smaller classes. This makes the subsystem more reusable and easier to customize, but it also becomes harder to use for clients that don't need to customize it. A facade can provide a simple default view of the subsystem that is good enough for most clients. Only clients needing more customizability will need to look beyond the facade.
-* there are many dependencies between clients and the implementation classes of an abstraction. Introduce a facade to decouple the subsystem from clients and other subsystems, thereby promoting subsystem independence and portability.
-* you want to layer your subsystems. Use a facade to define an entry point to each subsystem level. If subsystems are dependent, then you can simplify the dependencies between them by making them communicate with each other solely through their facades.
+* You want to provide a simple interface to a complex subsystem. Subsystems often get more complex
+as they evolve. Most patterns, when applied, result in more and smaller classes. This makes the
+subsystem more reusable and easier to customize, but it also becomes harder to use for clients that
+don't need to customize it. A facade can provide a simple default view of the subsystem that is good
+enough for most clients. Only clients needing more customization will need to look beyond the
+facade.
+* There are many dependencies between clients and the implementation classes of an abstraction.
+Introduce a facade to decouple the subsystem from clients and other subsystems, thereby promoting
+subsystem independence and portability.
+* You want to layer your subsystems. Use a facade to define an entry point to each subsystem level.
+If subsystems are dependent, then you can simplify the dependencies between them by making them
+communicate with each other solely through their facades.
## Credits
diff --git a/facade/pom.xml b/facade/pom.xml
index a7fdb88f0..cf73e6a43 100644
--- a/facade/pom.xml
+++ b/facade/pom.xml
@@ -29,7 +29,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
facade
diff --git a/facade/src/main/java/com/iluwatar/facade/module-info.java b/facade/src/main/java/com/iluwatar/facade/module-info.java
deleted file mode 100644
index 966758790..000000000
--- a/facade/src/main/java/com/iluwatar/facade/module-info.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.
- */
-
-module com.iluwatar.facade {
- requires org.slf4j;
-}
\ No newline at end of file
diff --git a/facade/src/test/java/com/iluwatar/facade/AppTest.java b/facade/src/test/java/com/iluwatar/facade/AppTest.java
index b6287b02b..7e2d389dc 100644
--- a/facade/src/test/java/com/iluwatar/facade/AppTest.java
+++ b/facade/src/test/java/com/iluwatar/facade/AppTest.java
@@ -25,13 +25,15 @@ package com.iluwatar.facade;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Application test
*/
-public class AppTest {
+class AppTest {
@Test
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/facade/src/test/java/com/iluwatar/facade/DwarvenGoldmineFacadeTest.java b/facade/src/test/java/com/iluwatar/facade/DwarvenGoldmineFacadeTest.java
index 3b67f3754..10d6e1ecd 100644
--- a/facade/src/test/java/com/iluwatar/facade/DwarvenGoldmineFacadeTest.java
+++ b/facade/src/test/java/com/iluwatar/facade/DwarvenGoldmineFacadeTest.java
@@ -110,7 +110,7 @@ public class DwarvenGoldmineFacadeTest {
private class InMemoryAppender extends AppenderBase {
- private List log = new LinkedList<>();
+ private final List log = new LinkedList<>();
public InMemoryAppender() {
((Logger) LoggerFactory.getLogger("root")).addAppender(this);
diff --git a/factory-kit/pom.xml b/factory-kit/pom.xml
index 87f27b341..987bbdb24 100644
--- a/factory-kit/pom.xml
+++ b/factory-kit/pom.xml
@@ -30,7 +30,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
factory-kit
diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/module-info.java b/factory-kit/src/main/java/com/iluwatar/factorykit/module-info.java
deleted file mode 100644
index 9440571c4..000000000
--- a/factory-kit/src/main/java/com/iluwatar/factorykit/module-info.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.
- */
-
-module com.iluwatar.factorykit {
- requires org.slf4j;
-}
\ No newline at end of file
diff --git a/factory-kit/src/test/java/com/iluwatar/factorykit/app/AppTest.java b/factory-kit/src/test/java/com/iluwatar/factorykit/app/AppTest.java
index f1d3c65a2..99477aaf0 100644
--- a/factory-kit/src/test/java/com/iluwatar/factorykit/app/AppTest.java
+++ b/factory-kit/src/test/java/com/iluwatar/factorykit/app/AppTest.java
@@ -26,14 +26,16 @@ package com.iluwatar.factorykit.app;
import com.iluwatar.factorykit.App;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Application Test Entrypoint
*/
-public class AppTest {
+class AppTest {
@Test
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/factory-method/README.md b/factory-method/README.md
index 206388537..180ddb868 100644
--- a/factory-method/README.md
+++ b/factory-method/README.md
@@ -10,17 +10,20 @@ tags:
---
## Also known as
+
Virtual Constructor
## Intent
-Define an interface for creating an object, but let subclasses
-decide which class to instantiate. Factory Method lets a class defer
-instantiation to subclasses.
+
+Define an interface for creating an object, but let subclasses decide which class to instantiate.
+Factory Method lets a class defer instantiation to subclasses.
## Explanation
+
Real world example
-> Blacksmith manufactures weapons. Elves require Elvish weapons and orcs require Orcish weapons. Depending on the customer at hand the right type of blacksmith is summoned.
+> Blacksmith manufactures weapons. Elves require Elvish weapons and orcs require Orcish weapons.
+> Depending on the customer at hand the right type of blacksmith is summoned.
In plain words
@@ -28,11 +31,16 @@ In plain words
Wikipedia says
-> In class-based programming, the factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created. This is done by creating objects by calling a factory method—either specified in an interface and implemented by child classes, or implemented in a base class and optionally overridden by derived classes—rather than by calling a constructor.
+> In class-based programming, the factory method pattern is a creational pattern that uses factory
+> methods to deal with the problem of creating objects without having to specify the exact class of
+> the object that will be created. This is done by creating objects by calling a factory method
+> — either specified in an interface and implemented by child classes, or implemented in a base
+> class and optionally overridden by derived classes—rather than by calling a constructor.
**Programmatic Example**
-Taking our blacksmith example above. First of all we have a blacksmith interface and some implementations for it
+Taking our blacksmith example above. First of all we have a `Blacksmith` interface and some
+implementations for it:
```java
public interface Blacksmith {
@@ -52,24 +60,33 @@ public class OrcBlacksmith implements Blacksmith {
}
```
-Now as the customers come the correct type of blacksmith is summoned and requested weapons are manufactured
+When the customers come, the correct type of blacksmith is summoned and requested weapons are
+manufactured:
```java
var blacksmith = new ElfBlacksmith();
blacksmith.manufactureWeapon(WeaponType.SPEAR);
blacksmith.manufactureWeapon(WeaponType.AXE);
-// Elvish weapons are created
+```
+
+Program output:
+```java
+// Elven spear
+// Elven axe
```
## Class diagram
+

## Applicability
-Use the Factory Method pattern when
-* a class can't anticipate the class of objects it must create
-* a class wants its subclasses to specify the objects it creates
-* classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate
+Use the Factory Method pattern when:
+
+* Class cannot anticipate the class of objects it must create.
+* Class wants its subclasses to specify the objects it creates.
+* Classes delegate responsibility to one of several helper subclasses, and you want to localize the
+knowledge of which helper subclass is the delegate.
## Real world examples
diff --git a/factory-method/pom.xml b/factory-method/pom.xml
index 5f0358d4d..c49fae691 100644
--- a/factory-method/pom.xml
+++ b/factory-method/pom.xml
@@ -29,7 +29,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
factory-method
diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/ElfBlacksmith.java b/factory-method/src/main/java/com/iluwatar/factory/method/ElfBlacksmith.java
index b6f29e43a..99ebcef65 100644
--- a/factory-method/src/main/java/com/iluwatar/factory/method/ElfBlacksmith.java
+++ b/factory-method/src/main/java/com/iluwatar/factory/method/ElfBlacksmith.java
@@ -32,7 +32,7 @@ import java.util.Map;
*/
public class ElfBlacksmith implements Blacksmith {
- private static Map ELFARSENAL;
+ private static final Map ELFARSENAL;
static {
ELFARSENAL = new HashMap<>(WeaponType.values().length);
diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/ElfWeapon.java b/factory-method/src/main/java/com/iluwatar/factory/method/ElfWeapon.java
index 66a6ea7e7..208dfa277 100644
--- a/factory-method/src/main/java/com/iluwatar/factory/method/ElfWeapon.java
+++ b/factory-method/src/main/java/com/iluwatar/factory/method/ElfWeapon.java
@@ -28,7 +28,7 @@ package com.iluwatar.factory.method;
*/
public class ElfWeapon implements Weapon {
- private WeaponType weaponType;
+ private final WeaponType weaponType;
public ElfWeapon(WeaponType weaponType) {
this.weaponType = weaponType;
diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/OrcBlacksmith.java b/factory-method/src/main/java/com/iluwatar/factory/method/OrcBlacksmith.java
index b04830085..ea99200de 100644
--- a/factory-method/src/main/java/com/iluwatar/factory/method/OrcBlacksmith.java
+++ b/factory-method/src/main/java/com/iluwatar/factory/method/OrcBlacksmith.java
@@ -32,7 +32,7 @@ import java.util.Map;
*/
public class OrcBlacksmith implements Blacksmith {
- private static Map ORCARSENAL;
+ private static final Map ORCARSENAL;
static {
ORCARSENAL = new HashMap<>(WeaponType.values().length);
diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/OrcWeapon.java b/factory-method/src/main/java/com/iluwatar/factory/method/OrcWeapon.java
index b35adf798..af1ee5bcf 100644
--- a/factory-method/src/main/java/com/iluwatar/factory/method/OrcWeapon.java
+++ b/factory-method/src/main/java/com/iluwatar/factory/method/OrcWeapon.java
@@ -28,7 +28,7 @@ package com.iluwatar.factory.method;
*/
public class OrcWeapon implements Weapon {
- private WeaponType weaponType;
+ private final WeaponType weaponType;
public OrcWeapon(WeaponType weaponType) {
this.weaponType = weaponType;
diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/WeaponType.java b/factory-method/src/main/java/com/iluwatar/factory/method/WeaponType.java
index 73ab10dd6..6c7c86712 100644
--- a/factory-method/src/main/java/com/iluwatar/factory/method/WeaponType.java
+++ b/factory-method/src/main/java/com/iluwatar/factory/method/WeaponType.java
@@ -30,7 +30,7 @@ public enum WeaponType {
SHORT_SWORD("short sword"), SPEAR("spear"), AXE("axe"), UNDEFINED("");
- private String title;
+ private final String title;
WeaponType(String title) {
this.title = title;
diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/module-info.java b/factory-method/src/main/java/com/iluwatar/factory/method/module-info.java
deleted file mode 100644
index 4ea385c8b..000000000
--- a/factory-method/src/main/java/com/iluwatar/factory/method/module-info.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.
- */
-
-module com.iluwatar.factorymethod {
- requires org.slf4j;
-}
\ No newline at end of file
diff --git a/factory-method/src/test/java/com/iluwatar/factory/method/AppTest.java b/factory-method/src/test/java/com/iluwatar/factory/method/AppTest.java
index c23295d9a..8756ba0aa 100644
--- a/factory-method/src/test/java/com/iluwatar/factory/method/AppTest.java
+++ b/factory-method/src/test/java/com/iluwatar/factory/method/AppTest.java
@@ -25,12 +25,15 @@ package com.iluwatar.factory.method;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Tests that Factory Method example runs without errors.
*/
-public class AppTest {
+class AppTest {
+
@Test
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/factory/README.md b/factory/README.md
new file mode 100644
index 000000000..a1b6208fd
--- /dev/null
+++ b/factory/README.md
@@ -0,0 +1,133 @@
+---
+layout: pattern
+title: Factory
+folder: factory
+permalink: /patterns/factory/
+categories: Creational
+tags:
+ - Gang of Four
+---
+
+## Also known as
+
+* Simple Factory
+* Static Factory Method
+
+## Intent
+
+Providing a static method encapsulated in a class called factory, in order to hide the
+implementation logic and makes client code focus on usage rather then initialization new objects.
+
+## Explanation
+
+Real world example
+
+> Lets say we have a web application connected to SQLServer, but now we want to switch to Oracle. To
+> do so without modifying existing source code, we need to implements Simple Factory pattern, in
+> which a static method can be invoked to create connection to a given database.
+
+Wikipedia says
+
+> Factory is an object for creating other objects – formally a factory is a function or method that
+> returns objects of a varying prototype or class.
+
+**Programmatic Example**
+
+We have an interface `Car` and two implementations `Ford` and `Ferrari`.
+
+```java
+public interface Car {
+ String getDescription();
+}
+
+public class Ford implements Car {
+
+ static final String DESCRIPTION = "This is Ford.";
+
+ @Override
+ public String getDescription() {
+ return DESCRIPTION;
+ }
+}
+
+public class Ferrari implements Car {
+
+ static final String DESCRIPTION = "This is Ferrari.";
+
+ @Override
+ public String getDescription() {
+ return DESCRIPTION;
+ }
+}
+```
+
+Enumeration above represents types of cars that we support (`Ford` and `Ferrari`).
+
+```java
+public enum CarType {
+
+ FORD(Ford::new),
+ FERRARI(Ferrari::new);
+
+ private final Supplier constructor;
+
+ CarType(Supplier constructor) {
+ this.constructor = constructor;
+ }
+
+ public Supplier getConstructor() {
+ return this.constructor;
+ }
+}
+```
+Then we have the static method `getCar` to create car objects encapsulated in the factory class
+`CarSimpleFactory`.
+
+```java
+public class CarsFactory {
+
+ public static Car getCar(CarType type) {
+ return type.getConstructor().get();
+ }
+}
+```
+
+Now on the client code we can create different types of cars using the factory class.
+
+```java
+var car1 = CarsFactory.getCar(CarType.FORD);
+var car2 = CarsFactory.getCar(CarType.FERRARI);
+LOGGER.info(car1.getDescription());
+LOGGER.info(car2.getDescription());;
+```
+
+Program output:
+
+```java
+This is Ford.
+This Ferrari.
+```
+
+## Class Diagram
+
+
+
+## Applicability
+
+Use the Simple Factory pattern when you only care about the creation of a object, not how to create
+and manage it.
+
+Pros
+
+* Allows keeping all objects creation in one place and avoid of spreading 'new' key value across codebase.
+* Allows to writs loosely coupled code. Some of its main advantages include better testability, easy-to-understand code, swappable components, scalability and isolated features.
+
+Cons
+
+* The code becomes more complicated than it should be.
+
+## Related patterns
+
+* [Factory Method](https://java-design-patterns.com/patterns/factory-method/)
+* [Factory Kit](https://java-design-patterns.com/patterns/factory-kit/)
+* [Abstract Factory](https://java-design-patterns.com/patterns/abstract-factory/)
diff --git a/factory/etc/factory.urm.png b/factory/etc/factory.urm.png
new file mode 100644
index 000000000..2ba39a571
Binary files /dev/null and b/factory/etc/factory.urm.png differ
diff --git a/factory/etc/factory.urm.puml b/factory/etc/factory.urm.puml
new file mode 100644
index 000000000..9eded6328
--- /dev/null
+++ b/factory/etc/factory.urm.puml
@@ -0,0 +1,35 @@
+@startuml
+package com.iluwatar.factory {
+ class App {
+ - LOGGER : Logger {static}
+ + App()
+ + main(args : String[]) {static}
+ }
+ interface Car {
+ + getDescription() : String {abstract}
+ }
+ class CarsFactory {
+ + CarsFactory()
+ + getCar(type : CarType) : Car {static}
+ }
+ ~enum CarType {
+ + FERRARI {static}
+ + FORD {static}
+ + valueOf(name : String) : CarType {static}
+ + values() : CarType[] {static}
+ }
+ class Ferrari {
+ ~ DESCRIPTION : String {static}
+ + Ferrari()
+ + getDescription() : String
+ }
+ class Ford {
+ ~ DESCRIPTION : String {static}
+ + Ford()
+ + getDescription() : String
+ }
+}
+CarType ..+ CarsFactory
+Ferrari ..|> Car
+Ford ..|> Car
+@enduml
\ No newline at end of file
diff --git a/factory/pom.xml b/factory/pom.xml
new file mode 100644
index 000000000..17c6863af
--- /dev/null
+++ b/factory/pom.xml
@@ -0,0 +1,67 @@
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.24.0-SNAPSHOT
+
+ factory
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ junit
+ junit
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.factory.App
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/factory/src/main/java/com/iluwatar/factory/App.java b/factory/src/main/java/com/iluwatar/factory/App.java
new file mode 100644
index 000000000..22c50d86d
--- /dev/null
+++ b/factory/src/main/java/com/iluwatar/factory/App.java
@@ -0,0 +1,51 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.factory;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Factory is an object for creating other objects, it providing Providing a static method to
+ * create and return objects of varying classes, in order to hide the implementation logic
+ * and makes client code focus on usage rather then objects initialization and management.
+ *
+ * In this example the CarFactory is the factory class and it provides a static method to
+ * create different cars.
+ */
+
+public class App {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
+
+ /**
+ * Program main entry point.
+ */
+ public static void main(String[] args) {
+ var car1 = CarsFactory.getCar(CarType.FORD);
+ var car2 = CarsFactory.getCar(CarType.FERRARI);
+ LOGGER.info(car1.getDescription());
+ LOGGER.info(car2.getDescription());
+ }
+}
diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/module-info.java b/factory/src/main/java/com/iluwatar/factory/Car.java
similarity index 90%
rename from abstract-document/src/main/java/com/iluwatar/abstractdocument/module-info.java
rename to factory/src/main/java/com/iluwatar/factory/Car.java
index 9121f0049..22b615342 100644
--- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/module-info.java
+++ b/factory/src/main/java/com/iluwatar/factory/Car.java
@@ -21,6 +21,13 @@
* THE SOFTWARE.
*/
-module com.iluwatar.abstractdocument {
- requires org.slf4j;
-}
\ No newline at end of file
+package com.iluwatar.factory;
+
+/**
+ * Car interface.
+ */
+public interface Car {
+
+ String getDescription();
+
+}
diff --git a/factory/src/main/java/com/iluwatar/factory/CarType.java b/factory/src/main/java/com/iluwatar/factory/CarType.java
new file mode 100644
index 000000000..4855cb89b
--- /dev/null
+++ b/factory/src/main/java/com/iluwatar/factory/CarType.java
@@ -0,0 +1,45 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.factory;
+
+import java.util.function.Supplier;
+
+public enum CarType {
+
+ /**
+ * Enumeration for different types of cars.
+ */
+ FORD(Ford::new),
+ FERRARI(Ferrari::new);
+
+ private final Supplier constructor;
+
+ CarType(Supplier constructor) {
+ this.constructor = constructor;
+ }
+
+ public Supplier getConstructor() {
+ return this.constructor;
+ }
+}
diff --git a/factory/src/main/java/com/iluwatar/factory/CarsFactory.java b/factory/src/main/java/com/iluwatar/factory/CarsFactory.java
new file mode 100644
index 000000000..0274ecc2e
--- /dev/null
+++ b/factory/src/main/java/com/iluwatar/factory/CarsFactory.java
@@ -0,0 +1,37 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.factory;
+
+/**
+ * Factory of cars.
+ */
+public class CarsFactory {
+
+ /**
+ * Factory method takes as parameter a car type and initiate the appropriate class.
+ */
+ public static Car getCar(CarType type) {
+ return type.getConstructor().get();
+ }
+}
diff --git a/factory/src/main/java/com/iluwatar/factory/Ferrari.java b/factory/src/main/java/com/iluwatar/factory/Ferrari.java
new file mode 100644
index 000000000..03a91ec28
--- /dev/null
+++ b/factory/src/main/java/com/iluwatar/factory/Ferrari.java
@@ -0,0 +1,37 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.factory;
+
+/**
+ * Ferrari implementation.
+ */
+public class Ferrari implements Car {
+
+ static final String DESCRIPTION = "This is Ferrari.";
+
+ @Override
+ public String getDescription() {
+ return DESCRIPTION;
+ }
+}
diff --git a/factory/src/main/java/com/iluwatar/factory/Ford.java b/factory/src/main/java/com/iluwatar/factory/Ford.java
new file mode 100644
index 000000000..9a94bb04f
--- /dev/null
+++ b/factory/src/main/java/com/iluwatar/factory/Ford.java
@@ -0,0 +1,37 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.factory;
+
+/**
+ * Ford implementation.
+ */
+public class Ford implements Car {
+
+ static final String DESCRIPTION = "This is Ford.";
+
+ @Override
+ public String getDescription() {
+ return DESCRIPTION;
+ }
+}
diff --git a/factory/src/test/java/com/iluwatar/factory/AppTest.java b/factory/src/test/java/com/iluwatar/factory/AppTest.java
new file mode 100644
index 000000000..1bc16a734
--- /dev/null
+++ b/factory/src/test/java/com/iluwatar/factory/AppTest.java
@@ -0,0 +1,37 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.factory;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+class AppTest {
+
+ @Test
+ void shouldExecuteWithoutExceptions() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
+ }
+
+}
diff --git a/factory/src/test/java/com/iluwatar/factory/CarsFactoryTest.java b/factory/src/test/java/com/iluwatar/factory/CarsFactoryTest.java
new file mode 100644
index 000000000..f69bd1632
--- /dev/null
+++ b/factory/src/test/java/com/iluwatar/factory/CarsFactoryTest.java
@@ -0,0 +1,38 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.factory;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+class CarsFactoryTest {
+
+ @Test
+ void shouldReturnFerrariInstance() {
+ final var ferrari = CarsFactory.getCar(CarType.FERRARI);
+ assertTrue(ferrari instanceof Ferrari);
+ }
+
+}
diff --git a/feature-toggle/pom.xml b/feature-toggle/pom.xml
index 13f646b80..1818d882d 100644
--- a/feature-toggle/pom.xml
+++ b/feature-toggle/pom.xml
@@ -29,7 +29,7 @@
java-design-patterns
com.iluwatar
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
4.0.0
diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/module-info.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/module-info.java
deleted file mode 100644
index 55c2d7714..000000000
--- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/module-info.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2019 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.
- */
-
-module com.iluwatar.featuretoggle {
- requires org.slf4j;
-}
\ No newline at end of file
diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java
index 6e2281b9a..ed6e69518 100644
--- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java
+++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java
@@ -42,7 +42,7 @@ import java.util.Properties;
*/
public class PropertiesFeatureToggleVersion implements Service {
- private boolean isEnhanced;
+ private final boolean isEnhanced;
/**
* Creates an instance of {@link PropertiesFeatureToggleVersion} using the passed {@link
diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java
index 5c660ca59..7924f86e8 100644
--- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java
+++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java
@@ -29,7 +29,7 @@ package com.iluwatar.featuretoggle.user;
*/
public class User {
- private String name;
+ private final String name;
/**
* Default Constructor setting the username.
diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java
index 524ea6ef8..7b644afd7 100644
--- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java
+++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java
@@ -35,8 +35,8 @@ import java.util.List;
*/
public class UserGroup {
- private static List freeGroup = new ArrayList<>();
- private static List paidGroup = new ArrayList<>();
+ private static final List freeGroup = new ArrayList<>();
+ private static final List paidGroup = new ArrayList<>();
/**
diff --git a/filterer/README.md b/filterer/README.md
new file mode 100644
index 000000000..89dc87e84
--- /dev/null
+++ b/filterer/README.md
@@ -0,0 +1,239 @@
+---
+layout: pattern
+title: Filterer
+folder: filterer
+permalink: /patterns/filterer/
+description: Design pattern that helps container-like objects to return filtered version of themselves.# short meta description that shows in Google search results
+categories:
+ - Functional
+tags:
+ - Extensibility
+---
+
+## Name / classification
+
+Filterer
+
+## Intent
+
+The intent of this design pattern is to introduce a functional interface that will add a
+functionality for container-like objects to easily return filtered versions of themselves.
+
+## Explanation
+
+Real world example
+
+> We are designing a threat (malware) detection software which can analyze target systems for
+> threats that are present in it. In the design we have to take into consideration that new
+> Threat types can be added later. Additionally, there is a requirement that the threat detection
+> system can filter the detected threats based on different criteria (the target system acts as
+> container-like object for threats).
+
+In plain words
+
+> Filterer pattern is a design pattern that helps container-like objects return filtered versions
+> of themselves.
+
+**Programmatic Example**
+
+To model the threat detection example presented above we introduce `Threat` and `ThreatAwareSystem`
+interfaces.
+
+```java
+public interface Threat {
+ String name();
+ int id();
+ ThreatType type();
+}
+
+public interface ThreatAwareSystem {
+ String systemId();
+ List extends Threat> threats();
+ Filterer extends ThreatAwareSystem, ? extends Threat> filtered();
+
+}
+```
+
+Notice the `filtered` method that returns instance of `Filterer` interface which is defined as:
+
+```java
+@FunctionalInterface
+public interface Filterer {
+ G by(Predicate super E> predicate);
+}
+```
+
+It is used to fulfill the requirement for system to be able to filter itself based on threat
+properties. The container-like object (`ThreatAwareSystem` in our case) needs to have a method that
+returns an instance of `Filterer`. This helper interface gives ability to covariantly specify a
+lower bound of contravariant `Predicate` in the subinterfaces of interfaces representing the
+container-like objects.
+
+In our example we will be able to pass a predicate that takes `? extends Threat` object and
+return `? extends ThreatAwareSystem` from `Filtered::by` method. A simple implementation
+of `ThreatAwareSystem`:
+
+```java
+public class SimpleThreatAwareSystem implements ThreatAwareSystem {
+
+ private final String systemId;
+ private final ImmutableList issues;
+
+ public SimpleThreatAwareSystem(final String systemId, final List issues) {
+ this.systemId = systemId;
+ this.issues = ImmutableList.copyOf(issues);
+ }
+
+ @Override
+ public String systemId() {
+ return systemId;
+ }
+
+ @Override
+ public List extends Threat> threats() {
+ return new ArrayList<>(issues);
+ }
+
+ @Override
+ public Filterer extends ThreatAwareSystem, ? extends Threat> filtered() {
+ return this::filteredGroup;
+ }
+
+ private ThreatAwareSystem filteredGroup(Predicate super Threat> predicate) {
+ return new SimpleThreatAwareSystem(this.systemId, filteredItems(predicate));
+ }
+
+ private List filteredItems(Predicate super Threat> predicate) {
+ return this.issues.stream()
+ .filter(predicate)
+ .collect(Collectors.toList());
+ }
+}
+```
+
+The `filtered` method is overridden to filter the threats list by given predicate.
+
+Now if we introduce a new subtype of `Threat` interface that adds probability with which given
+threat can appear:
+
+```java
+public interface ProbableThreat extends Threat {
+ double probability();
+}
+```
+
+We can also introduce a new interface that represents a system that is aware of threats with their
+probabilities:
+
+````java
+public interface ProbabilisticThreatAwareSystem extends ThreatAwareSystem {
+ @Override
+ List extends ProbableThreat> threats();
+
+ @Override
+ Filterer extends ProbabilisticThreatAwareSystem, ? extends ProbableThreat> filtered();
+}
+````
+
+Notice how we override the `filtered` method in `ProbabilisticThreatAwareSystem` and specify
+different return covariant type by specifying different generic types. Our interfaces are clean and
+not cluttered by default implementations. We we will be able to filter
+`ProbabilisticThreatAwareSystem` by `ProbableThreat` properties:
+
+```java
+public class SimpleProbabilisticThreatAwareSystem implements ProbabilisticThreatAwareSystem {
+
+ private final String systemId;
+ private final ImmutableList threats;
+
+ public SimpleProbabilisticThreatAwareSystem(final String systemId, final List threats) {
+ this.systemId = systemId;
+ this.threats = ImmutableList.copyOf(threats);
+ }
+
+ @Override
+ public String systemId() {
+ return systemId;
+ }
+
+ @Override
+ public List extends ProbableThreat> threats() {
+ return threats;
+ }
+
+ @Override
+ public Filterer extends ProbabilisticThreatAwareSystem, ? extends ProbableThreat> filtered() {
+ return this::filteredGroup;
+ }
+
+ private ProbabilisticThreatAwareSystem filteredGroup(final Predicate super ProbableThreat> predicate) {
+ return new SimpleProbabilisticThreatAwareSystem(this.systemId, filteredItems(predicate));
+ }
+
+ private List filteredItems(final Predicate super ProbableThreat> predicate) {
+ return this.threats.stream()
+ .filter(predicate)
+ .collect(Collectors.toList());
+ }
+}
+```
+
+Now if we want filter `ThreatAwareSystem` by threat type we can do:
+
+```java
+Threat rootkit = new SimpleThreat(ThreatType.ROOTKIT, 1, "Simple-Rootkit");
+Threat trojan = new SimpleThreat(ThreatType.TROJAN, 2, "Simple-Trojan");
+List threats = List.of(rootkit, trojan);
+
+ThreatAwareSystem threatAwareSystem = new SimpleThreatAwareSystem("System-1", threats);
+
+ThreatAwareSystem rootkitThreatAwareSystem = threatAwareSystem.filtered()
+ .by(threat -> threat.type() == ThreatType.ROOTKIT);
+```
+
+Or if we want to filter `ProbabilisticThreatAwareSystem`:
+
+```java
+ProbableThreat malwareTroyan = new SimpleProbableThreat("Troyan-ArcBomb", 1, ThreatType.TROJAN, 0.99);
+ProbableThreat rootkit = new SimpleProbableThreat("Rootkit-System", 2, ThreatType.ROOTKIT, 0.8);
+List probableThreats = List.of(malwareTroyan, rootkit);
+
+ProbabilisticThreatAwareSystem simpleProbabilisticThreatAwareSystem =new SimpleProbabilisticThreatAwareSystem("System-1", probableThreats);
+
+ProbabilisticThreatAwareSystem filtered = simpleProbabilisticThreatAwareSystem.filtered()
+ .by(probableThreat -> Double.compare(probableThreat.probability(), 0.99) == 0);
+```
+
+## Class diagram
+
+
+
+## Applicability
+
+Pattern can be used when working with container-like objects that use subtyping, instead of
+parametrizing (generics) for extensible class structure. It enables you to easily extend filtering
+ability of container-like objects as business requirements change.
+
+## Tutorials
+
+* [Article about Filterer pattern posted on it's author's blog](https://blog.tlinkowski.pl/2018/filterer-pattern/)
+* [Application of Filterer pattern in domain of text analysis](https://www.javacodegeeks.com/2019/02/filterer-pattern-10-steps.html)
+
+## Known uses
+
+One of the uses is present on the blog presented in
+[this](https://www.javacodegeeks.com/2019/02/filterer-pattern-10-steps.html) link. It presents how
+to use `Filterer` pattern to create text issue analyzer with support for test cases used for unit
+testing.
+
+## Consequences
+
+Pros:
+ * You can easily introduce new subtypes for container-like objects and subtypes for objects that are contained within them and still be able to filter easily be new properties of those new subtypes.
+
+Cons:
+ * Covariant return types mixed with generics can be sometimes tricky
+
+## Credits
+
+* Author of the pattern : [Tomasz Linkowski](https://tlinkowski.pl/)
diff --git a/filterer/etc/filterer.png b/filterer/etc/filterer.png
new file mode 100644
index 000000000..6a6eb059b
Binary files /dev/null and b/filterer/etc/filterer.png differ
diff --git a/filterer/etc/filterer.urm.puml b/filterer/etc/filterer.urm.puml
new file mode 100644
index 000000000..c0bb0b54d
--- /dev/null
+++ b/filterer/etc/filterer.urm.puml
@@ -0,0 +1,96 @@
+@startuml
+package com.iluwatar.filterer.domain {
+ interface Filterer {
+ + by(Predicate super E>) : G {abstract}
+ }
+}
+package com.iluwatar.filterer {
+ class App {
+ - LOGGER : Logger {static}
+ + App()
+ - filteringSimpleProbableThreats() {static}
+ - filteringSimpleThreats() {static}
+ + main(args : String[]) {static}
+ }
+}
+package com.iluwatar.filterer.threat {
+ interface ProbabilisticThreatAwareSystem {
+ + filtered() : Filterer extends ProbabilisticThreatAwareSystem, ? extends ProbableThreat> {abstract}
+ + threats() : List extends ProbableThreat> {abstract}
+ }
+ interface ProbableThreat {
+ + probability() : double {abstract}
+ }
+ class SimpleProbabilisticThreatAwareSystem {
+ - systemId : String
+ - threats : ImmutableList
+ + SimpleProbabilisticThreatAwareSystem(systemId : String, threats : List)
+ + equals(o : Object) : boolean
+ + filtered() : Filterer extends ProbabilisticThreatAwareSystem, ? extends ProbableThreat>
+ - filteredGroup(predicate : Predicate super ProbableThreat>) : ProbabilisticThreatAwareSystem
+ - filteredItems(predicate : Predicate super ProbableThreat>) : List
+ + hashCode() : int
+ + systemId() : String
+ + threats() : List extends ProbableThreat>
+ + toString() : String
+ }
+ class SimpleProbableThreat {
+ - probability : double
+ + SimpleProbableThreat(name : String, id : int, threatType : ThreatType, probability : double)
+ + equals(o : Object) : boolean
+ + hashCode() : int
+ + probability() : double
+ + toString() : String
+ }
+ class SimpleThreat {
+ - id : int
+ - name : String
+ - threatType : ThreatType
+ + SimpleThreat(threatType : ThreatType, id : int, name : String)
+ + id() : int
+ + name() : String
+ + toString() : String
+ + type() : ThreatType
+ }
+ class SimpleThreatAwareSystem {
+ - issues : ImmutableList
+ - systemId : String
+ + SimpleThreatAwareSystem(systemId : String, issues : List)
+ + equals(o : Object) : boolean
+ + filtered() : Filterer extends ThreatAwareSystem, ? extends Threat>
+ - filteredGroup(predicate : Predicate super Threat>) : ThreatAwareSystem
+ - filteredItems(predicate : Predicate super Threat>) : List
+ + hashCode() : int
+ + systemId() : String
+ + threats() : List extends Threat>
+ + toString() : String
+ }
+ interface Threat {
+ + id() : int {abstract}
+ + name() : String {abstract}
+ + type() : ThreatType {abstract}
+ }
+ interface ThreatAwareSystem {
+ + filtered() : Filterer extends ThreatAwareSystem, ? extends Threat> {abstract}
+ + systemId() : String {abstract}
+ + threats() : List extends Threat> {abstract}
+ }
+ enum ThreatType {
+ + ROOTKIT {static}
+ + TROJAN {static}
+ + WORM {static}
+ + valueOf(name : String) : ThreatType {static}
+ + values() : ThreatType[] {static}
+ }
+}
+SimpleThreatAwareSystem --> "-issues" Threat
+SimpleThreat --> "-threatType" ThreatType
+SimpleProbabilisticThreatAwareSystem --> "-threats" ProbableThreat
+ProbabilisticThreatAwareSystem --|> ThreatAwareSystem
+ProbableThreat --|> Threat
+SimpleProbabilisticThreatAwareSystem ..|> ProbabilisticThreatAwareSystem
+SimpleProbableThreat ..|> ProbableThreat
+SimpleProbableThreat --|> SimpleThreat
+SimpleThreat ..|> Threat
+SimpleThreatAwareSystem ..|> ThreatAwareSystem
+@enduml
\ No newline at end of file
diff --git a/filterer/pom.xml b/filterer/pom.xml
new file mode 100644
index 000000000..46042c1b0
--- /dev/null
+++ b/filterer/pom.xml
@@ -0,0 +1,73 @@
+
+
+
+
+ java-design-patterns
+ com.iluwatar
+ 1.24.0-SNAPSHOT
+
+ 4.0.0
+
+ filterer
+
+
+
+ com.google.guava
+ guava
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.filterer.App
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/filterer/src/main/java/com/iluwatar/filterer/App.java b/filterer/src/main/java/com/iluwatar/filterer/App.java
new file mode 100644
index 000000000..43de5a646
--- /dev/null
+++ b/filterer/src/main/java/com/iluwatar/filterer/App.java
@@ -0,0 +1,108 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.filterer;
+
+import com.iluwatar.filterer.threat.ProbableThreat;
+import com.iluwatar.filterer.threat.SimpleProbabilisticThreatAwareSystem;
+import com.iluwatar.filterer.threat.SimpleProbableThreat;
+import com.iluwatar.filterer.threat.SimpleThreat;
+import com.iluwatar.filterer.threat.SimpleThreatAwareSystem;
+import com.iluwatar.filterer.threat.Threat;
+import com.iluwatar.filterer.threat.ThreatAwareSystem;
+import com.iluwatar.filterer.threat.ThreatType;
+
+import java.util.List;
+import java.util.function.Predicate;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This demo class represent how {@link com.iluwatar.filterer.domain.Filterer} pattern is used to
+ * filter container-like objects to return filtered versions of themselves. The container like
+ * objects are systems that are aware of threats that they can be vulnerable to. We would like
+ * to have a way to create copy of different system objects but with filtered threats.
+ * The thing is to keep it simple if we add new subtype of {@link Threat}
+ * (for example {@link ProbableThreat}) - we still need to be able to filter by it's properties.
+ */
+public class App {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
+
+ public static void main(String[] args) {
+ filteringSimpleThreats();
+ filteringSimpleProbableThreats();
+ }
+
+ /**
+ * Demonstrates how to filter {@link com.iluwatar.filterer.threat.ProbabilisticThreatAwareSystem}
+ * based on probability property. The @{@link com.iluwatar.filterer.domain.Filterer#by(Predicate)}
+ * method is able to use {@link com.iluwatar.filterer.threat.ProbableThreat}
+ * as predicate argument.
+ */
+ private static void filteringSimpleProbableThreats() {
+ LOGGER.info(" ### Filtering ProbabilisticThreatAwareSystem by probability ###");
+
+ var trojanArcBomb = new SimpleProbableThreat("Trojan-ArcBomb", 1, ThreatType.TROJAN, 0.99);
+ var rootkit = new SimpleProbableThreat("Rootkit-Kernel", 2, ThreatType.ROOTKIT, 0.8);
+
+ List probableThreats = List.of(trojanArcBomb, rootkit);
+
+ var probabilisticThreatAwareSystem =
+ new SimpleProbabilisticThreatAwareSystem("Sys-1", probableThreats);
+
+ LOGGER.info("Filtering ProbabilisticThreatAwareSystem. Initial : "
+ + probabilisticThreatAwareSystem);
+
+ //Filtering using filterer
+ var filteredThreatAwareSystem = probabilisticThreatAwareSystem.filtered()
+ .by(probableThreat -> Double.compare(probableThreat.probability(), 0.99) == 0);
+
+ LOGGER.info("Filtered by probability = 0.99 : " + filteredThreatAwareSystem);
+ }
+
+ /**
+ * Demonstrates how to filter {@link ThreatAwareSystem} based on startingOffset property
+ * of {@link SimpleThreat}. The @{@link com.iluwatar.filterer.domain.Filterer#by(Predicate)}
+ * method is able to use {@link Threat} as predicate argument.
+ */
+ private static void filteringSimpleThreats() {
+ LOGGER.info("### Filtering ThreatAwareSystem by ThreatType ###");
+
+ var rootkit = new SimpleThreat(ThreatType.ROOTKIT, 1, "Simple-Rootkit");
+ var trojan = new SimpleThreat(ThreatType.TROJAN, 2, "Simple-Trojan");
+ List threats = List.of(rootkit, trojan);
+
+ var threatAwareSystem = new SimpleThreatAwareSystem("Sys-1", threats);
+
+ LOGGER.info("Filtering ThreatAwareSystem. Initial : " + threatAwareSystem);
+
+ //Filtering using Filterer
+ var rootkitThreatAwareSystem = threatAwareSystem.filtered()
+ .by(threat -> threat.type() == ThreatType.ROOTKIT);
+
+ LOGGER.info("Filtered by threatType = ROOTKIT : " + rootkitThreatAwareSystem);
+ }
+
+}
diff --git a/filterer/src/main/java/com/iluwatar/filterer/domain/Filterer.java b/filterer/src/main/java/com/iluwatar/filterer/domain/Filterer.java
new file mode 100644
index 000000000..17970c115
--- /dev/null
+++ b/filterer/src/main/java/com/iluwatar/filterer/domain/Filterer.java
@@ -0,0 +1,36 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.filterer.domain;
+
+import java.util.function.Predicate;
+
+/**
+ * Filterer helper interface.
+ * @param type of the container-like object.
+ * @param type of the elements contained within this container-like object.
+ */
+@FunctionalInterface
+public interface Filterer {
+ G by(Predicate super E> predicate);
+}
\ No newline at end of file
diff --git a/filterer/src/main/java/com/iluwatar/filterer/threat/ProbabilisticThreatAwareSystem.java b/filterer/src/main/java/com/iluwatar/filterer/threat/ProbabilisticThreatAwareSystem.java
new file mode 100644
index 000000000..3a2959828
--- /dev/null
+++ b/filterer/src/main/java/com/iluwatar/filterer/threat/ProbabilisticThreatAwareSystem.java
@@ -0,0 +1,49 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.filterer.threat;
+
+import com.iluwatar.filterer.domain.Filterer;
+
+import java.util.List;
+
+/**
+ * Represents system that is aware of it's threats with given probability of their occurrence.
+ */
+public interface ProbabilisticThreatAwareSystem extends ThreatAwareSystem {
+
+ /**
+ * {@inheritDoc}
+ * @return
+ */
+ @Override
+ List extends ProbableThreat> threats();
+
+ /**
+ * {@inheritDoc}
+ * @return
+ */
+ @Override
+ Filterer extends ProbabilisticThreatAwareSystem, ? extends ProbableThreat> filtered();
+}
+
diff --git a/filterer/src/main/java/com/iluwatar/filterer/threat/ProbableThreat.java b/filterer/src/main/java/com/iluwatar/filterer/threat/ProbableThreat.java
new file mode 100644
index 000000000..11e61dbf6
--- /dev/null
+++ b/filterer/src/main/java/com/iluwatar/filterer/threat/ProbableThreat.java
@@ -0,0 +1,35 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.filterer.threat;
+
+/**
+ * Represents threat that might be a threat with given probability.
+ */
+public interface ProbableThreat extends Threat {
+ /**
+ * Returns probability of occurrence of given threat.
+ * @return probability of occurrence of given threat.
+ */
+ double probability();
+}
\ No newline at end of file
diff --git a/filterer/src/main/java/com/iluwatar/filterer/threat/SimpleProbabilisticThreatAwareSystem.java b/filterer/src/main/java/com/iluwatar/filterer/threat/SimpleProbabilisticThreatAwareSystem.java
new file mode 100644
index 000000000..3991d975e
--- /dev/null
+++ b/filterer/src/main/java/com/iluwatar/filterer/threat/SimpleProbabilisticThreatAwareSystem.java
@@ -0,0 +1,113 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.filterer.threat;
+
+import com.google.common.collect.ImmutableList;
+import com.iluwatar.filterer.domain.Filterer;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+/**
+ * {@inheritDoc}
+ */
+public class SimpleProbabilisticThreatAwareSystem implements ProbabilisticThreatAwareSystem {
+
+ private final String systemId;
+ private final ImmutableList threats;
+
+ public SimpleProbabilisticThreatAwareSystem(
+ final String systemId,
+ final List threats
+ ) {
+ this.systemId = systemId;
+ this.threats = ImmutableList.copyOf(threats);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String systemId() {
+ return systemId;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List extends ProbableThreat> threats() {
+ return threats;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Filterer extends ProbabilisticThreatAwareSystem, ? extends ProbableThreat> filtered() {
+ return this::filteredGroup;
+ }
+
+ private ProbabilisticThreatAwareSystem filteredGroup(
+ final Predicate super ProbableThreat> predicate
+ ) {
+ return new SimpleProbabilisticThreatAwareSystem(this.systemId, filteredItems(predicate));
+ }
+
+ private List filteredItems(
+ final Predicate super ProbableThreat> predicate
+ ) {
+ return this.threats.stream()
+ .filter(predicate)
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ var that = (SimpleProbabilisticThreatAwareSystem) o;
+ return systemId.equals(that.systemId)
+ && threats.equals(that.threats);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(systemId, threats);
+ }
+
+ @Override
+ public String toString() {
+ return "SimpleProbabilisticThreatAwareSystem{"
+ + "systemId='" + systemId + '\''
+ + ", threats=" + threats
+ + '}';
+ }
+}
diff --git a/filterer/src/main/java/com/iluwatar/filterer/threat/SimpleProbableThreat.java b/filterer/src/main/java/com/iluwatar/filterer/threat/SimpleProbableThreat.java
new file mode 100644
index 000000000..54da07873
--- /dev/null
+++ b/filterer/src/main/java/com/iluwatar/filterer/threat/SimpleProbableThreat.java
@@ -0,0 +1,79 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.filterer.threat;
+
+import java.util.Objects;
+
+/**
+ * {@inheritDoc}
+ */
+public class SimpleProbableThreat extends SimpleThreat implements ProbableThreat {
+
+ private final double probability;
+
+ public SimpleProbableThreat(final String name,
+ final int id,
+ final ThreatType threatType,
+ final double probability
+ ) {
+ super(threatType, id, name);
+ this.probability = probability;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public double probability() {
+ return probability;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+ var that = (SimpleProbableThreat) o;
+ return Double.compare(that.probability, probability) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), probability);
+ }
+
+ @Override
+ public String toString() {
+ return "SimpleProbableThreat{"
+ + "probability=" + probability
+ + "} "
+ + super.toString();
+ }
+}
diff --git a/filterer/src/main/java/com/iluwatar/filterer/threat/SimpleThreat.java b/filterer/src/main/java/com/iluwatar/filterer/threat/SimpleThreat.java
new file mode 100644
index 000000000..08a8b0e17
--- /dev/null
+++ b/filterer/src/main/java/com/iluwatar/filterer/threat/SimpleThreat.java
@@ -0,0 +1,101 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.filterer.threat;
+
+import java.util.Objects;
+
+/**
+ * Represents a simple threat.
+ */
+public class SimpleThreat implements Threat {
+
+ private final ThreatType threatType;
+ private final int id;
+ private final String name;
+
+ /**
+ * Constructor.
+ *
+ * @param threatType {@link ThreatType}.
+ * @param id threat id.
+ * @param name threat name.
+ */
+ public SimpleThreat(final ThreatType threatType, final int id, String name) {
+ this.threatType = threatType;
+ this.id = id;
+ this.name = name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String name() {
+ return name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int id() {
+ return id;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ThreatType type() {
+ return threatType;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ var that = (SimpleThreat) o;
+ return id == that.id
+ && threatType == that.threatType
+ && Objects.equals(name, that.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(threatType, id, name);
+ }
+
+ @Override
+ public String toString() {
+ return "SimpleThreat{"
+ + "threatType=" + threatType
+ + ", id=" + id
+ + ", name='" + name + '\''
+ + '}';
+ }
+}
diff --git a/filterer/src/main/java/com/iluwatar/filterer/threat/SimpleThreatAwareSystem.java b/filterer/src/main/java/com/iluwatar/filterer/threat/SimpleThreatAwareSystem.java
new file mode 100644
index 000000000..f1dec40ae
--- /dev/null
+++ b/filterer/src/main/java/com/iluwatar/filterer/threat/SimpleThreatAwareSystem.java
@@ -0,0 +1,107 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.filterer.threat;
+
+import com.google.common.collect.ImmutableList;
+import com.iluwatar.filterer.domain.Filterer;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+/**
+ * {@inheritDoc}
+ */
+public class SimpleThreatAwareSystem implements ThreatAwareSystem {
+
+ private final String systemId;
+ private final ImmutableList issues;
+
+ public SimpleThreatAwareSystem(final String systemId, final List issues) {
+ this.systemId = systemId;
+ this.issues = ImmutableList.copyOf(issues);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String systemId() {
+ return systemId;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List extends Threat> threats() {
+ return new ArrayList<>(issues);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Filterer extends ThreatAwareSystem, ? extends Threat> filtered() {
+ return this::filteredGroup;
+ }
+
+ private ThreatAwareSystem filteredGroup(Predicate super Threat> predicate) {
+ return new SimpleThreatAwareSystem(this.systemId, filteredItems(predicate));
+ }
+
+ private List filteredItems(Predicate super Threat> predicate) {
+ return this.issues.stream()
+ .filter(predicate)
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ var that = (SimpleThreatAwareSystem) o;
+ return systemId.equals(that.systemId)
+ && issues.equals(that.issues);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(systemId, issues);
+ }
+
+ @Override
+ public String toString() {
+ return "SimpleThreatAwareSystem{"
+ + "systemId='" + systemId
+ + '\'' + ", issues=" + issues
+ + '}';
+ }
+}
diff --git a/filterer/src/main/java/com/iluwatar/filterer/threat/Threat.java b/filterer/src/main/java/com/iluwatar/filterer/threat/Threat.java
new file mode 100644
index 000000000..515b59332
--- /dev/null
+++ b/filterer/src/main/java/com/iluwatar/filterer/threat/Threat.java
@@ -0,0 +1,49 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.filterer.threat;
+
+/**
+ * Represents a threat that can be detected in given system.
+ */
+public interface Threat {
+ /**
+ * Returns name of the threat.
+ *
+ * @return value representing name of the threat.
+ */
+ String name();
+
+ /**
+ * Returns unique id of the threat.
+ *
+ * @return value representing threat id.
+ */
+ int id();
+
+ /**
+ * Returns threat type.
+ * @return {@link ThreatType}
+ */
+ ThreatType type();
+}
diff --git a/filterer/src/main/java/com/iluwatar/filterer/threat/ThreatAwareSystem.java b/filterer/src/main/java/com/iluwatar/filterer/threat/ThreatAwareSystem.java
new file mode 100644
index 000000000..b889d537d
--- /dev/null
+++ b/filterer/src/main/java/com/iluwatar/filterer/threat/ThreatAwareSystem.java
@@ -0,0 +1,55 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.filterer.threat;
+
+import com.iluwatar.filterer.domain.Filterer;
+
+import java.util.List;
+
+/**
+ * Represents system that is aware of threats that are present in it.
+ */
+public interface ThreatAwareSystem {
+
+ /**
+ * Returns the system id.
+ *
+ * @return system id.
+ */
+ String systemId();
+
+ /**
+ * Returns list of threats for this system.
+ * @return list of threats for this system.
+ */
+ List extends Threat> threats();
+
+ /**
+ * Returns the instance of {@link Filterer} helper interface that allows to covariantly
+ * specify lower bound for predicate that we want to filter by.
+ * @return an instance of {@link Filterer} helper interface.
+ */
+ Filterer extends ThreatAwareSystem, ? extends Threat> filtered();
+
+}
diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/module-info.java b/filterer/src/main/java/com/iluwatar/filterer/threat/ThreatType.java
similarity index 92%
rename from business-delegate/src/main/java/com/iluwatar/business/delegate/module-info.java
rename to filterer/src/main/java/com/iluwatar/filterer/threat/ThreatType.java
index 8f331c848..5f9a152a8 100644
--- a/business-delegate/src/main/java/com/iluwatar/business/delegate/module-info.java
+++ b/filterer/src/main/java/com/iluwatar/filterer/threat/ThreatType.java
@@ -21,6 +21,6 @@
* THE SOFTWARE.
*/
-module com.iluwatar.business.delegate {
- requires org.slf4j;
-}
\ No newline at end of file
+package com.iluwatar.filterer.threat;
+
+public enum ThreatType { TROJAN, WORM, ROOTKIT }
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/module-info.java b/filterer/src/test/java/com/iluwatar/filterer/AppTest.java
similarity index 88%
rename from acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/module-info.java
rename to filterer/src/test/java/com/iluwatar/filterer/AppTest.java
index 78de5a786..551ebcc18 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/module-info.java
+++ b/filterer/src/test/java/com/iluwatar/filterer/AppTest.java
@@ -21,6 +21,13 @@
* THE SOFTWARE.
*/
-module com.iluwatar.acyclicvisitor {
- requires org.slf4j;
+package com.iluwatar.filterer;
+
+import org.junit.jupiter.api.Test;
+
+class AppTest {
+ @Test
+ void shouldLaunchApp() {
+ App.main(new String[]{});
+ }
}
\ No newline at end of file
diff --git a/filterer/src/test/java/com/iluwatar/filterer/threat/SimpleProbabilisticThreatAwareSystemTest.java b/filterer/src/test/java/com/iluwatar/filterer/threat/SimpleProbabilisticThreatAwareSystemTest.java
new file mode 100644
index 000000000..2f14ca057
--- /dev/null
+++ b/filterer/src/test/java/com/iluwatar/filterer/threat/SimpleProbabilisticThreatAwareSystemTest.java
@@ -0,0 +1,51 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.filterer.threat;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class SimpleProbabilisticThreatAwareSystemTest {
+ @Test
+ void shouldFilterByProbability() {
+ //given
+ var trojan = new SimpleProbableThreat("Troyan-ArcBomb", 1, ThreatType.TROJAN, 0.99);
+ var rootkit = new SimpleProbableThreat("Rootkit-System", 2, ThreatType.ROOTKIT, 0.8);
+ List probableThreats = List.of(trojan, rootkit);
+
+ var simpleProbabilisticThreatAwareSystem =
+ new SimpleProbabilisticThreatAwareSystem("System-1", probableThreats);
+
+ //when
+ var filtered = simpleProbabilisticThreatAwareSystem.filtered()
+ .by(probableThreat -> Double.compare(probableThreat.probability(), 0.99) == 0);
+
+ //then
+ assertEquals(filtered.threats().size(), 1);
+ assertEquals(filtered.threats().get(0), trojan);
+ }
+}
\ No newline at end of file
diff --git a/filterer/src/test/java/com/iluwatar/filterer/threat/SimpleThreatAwareSystemTest.java b/filterer/src/test/java/com/iluwatar/filterer/threat/SimpleThreatAwareSystemTest.java
new file mode 100644
index 000000000..ea918c9ec
--- /dev/null
+++ b/filterer/src/test/java/com/iluwatar/filterer/threat/SimpleThreatAwareSystemTest.java
@@ -0,0 +1,50 @@
+/*
+ * The MIT License
+ * Copyright © 2014-2019 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.filterer.threat;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class SimpleThreatAwareSystemTest {
+ @Test
+ void shouldFilterByThreatType() {
+ //given
+ var rootkit = new SimpleThreat(ThreatType.ROOTKIT, 1, "Simple-Rootkit");
+ var trojan = new SimpleThreat(ThreatType.TROJAN, 2, "Simple-Trojan");
+ List threats = List.of(rootkit, trojan);
+
+ var threatAwareSystem = new SimpleThreatAwareSystem("System-1", threats);
+
+ //when
+ var rootkitThreatAwareSystem = threatAwareSystem.filtered()
+ .by(threat -> threat.type() == ThreatType.ROOTKIT);
+
+ //then
+ assertEquals(rootkitThreatAwareSystem.threats().size(), 1);
+ assertEquals(rootkitThreatAwareSystem.threats().get(0), rootkit);
+ }
+}
\ No newline at end of file
diff --git a/fluentinterface/README.md b/fluentinterface/README.md
index 61c5f2eb5..8b3f80fae 100644
--- a/fluentinterface/README.md
+++ b/fluentinterface/README.md
@@ -9,23 +9,25 @@ tags:
---
## Intent
-A fluent interface provides an easy-readable, flowing interface, that often mimics a domain specific language. Using
-this pattern results in code that can be read nearly as human language.
+
+A fluent interface provides an easy-readable, flowing interface, that often mimics a domain specific
+language. Using this pattern results in code that can be read nearly as human language.
## Explanation
-The Fluent Interface pattern is useful when you want to provide an easy readable, flowing API. Those interfaces tend
-to mimic domain specific languages, so they can nearly be read as human languages.
+The Fluent Interface pattern is useful when you want to provide an easy readable, flowing API. Those
+interfaces tend to mimic domain specific languages, so they can nearly be read as human languages.
A fluent interface can be implemented using any of
- * Method Chaining - calling a method returns some object on which further methods can be called.
- * Static Factory Methods and Imports
+ * Method chaining - calling a method returns some object on which further methods can be called.
+ * Static factory methods and imports.
* Named parameters - can be simulated in Java using static factory methods.
Real world example
-> We need to select numbers based on different criteria from the list. It's a great chance to utilize fluent interface pattern to provide readable easy-to-use developer experience.
+> We need to select numbers based on different criteria from the list. It's a great chance to
+> utilize fluent interface pattern to provide readable easy-to-use developer experience.
In plain words
@@ -33,7 +35,9 @@ In plain words
Wikipedia says
-> In software engineering, a fluent interface is an object-oriented API whose design relies extensively on method chaining. Its goal is to increase code legibility by creating a domain-specific language (DSL).
+> In software engineering, a fluent interface is an object-oriented API whose design relies
+> extensively on method chaining. Its goal is to increase code legibility by creating a
+> domain-specific language (DSL).
**Programmatic Example**
@@ -134,29 +138,35 @@ result is printed afterwards.
.first(2)
.last()
.ifPresent(number -> LOGGER.info("Last amongst first two negatives: {}", number));
-
- // The initial list contains: 1, -61, 14, -22, 18, -87, 6, 64, -82, 26, -98, 97, 45, 23, 2, -68.
- // The first three negative values are: -61, -22, -87.
- // The last two positive values are: 23, 2.
- // The first even number is: 14
- // A string-mapped list of negative numbers contains: String[-61], String[-22], String[-87], String[-82], String[-98], String[-68].
- // The lazy list contains the last two of the first four positive numbers mapped to Strings: String[18], String[6].
- // Last amongst first two negatives: -22
+```
+
+Program output:
+
+```java
+The initial list contains: 1, -61, 14, -22, 18, -87, 6, 64, -82, 26, -98, 97, 45, 23, 2, -68.
+The first three negative values are: -61, -22, -87.
+The last two positive values are: 23, 2.
+The first even number is: 14
+A string-mapped list of negative numbers contains: String[-61], String[-22], String[-87], String[-82], String[-98], String[-68].
+The lazy list contains the last two of the first four positive numbers mapped to Strings: String[18], String[6].
+Last amongst first two negatives: -22
```
## Class diagram
+

## Applicability
+
Use the Fluent Interface pattern when
-* You provide an API that would benefit from a DSL-like usage
-* You have objects that are difficult to configure or use
+* You provide an API that would benefit from a DSL-like usage.
+* You have objects that are difficult to configure or use.
## Known uses
* [Java 8 Stream API](http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.html)
-* [Google Guava FluentInterable](https://github.com/google/guava/wiki/FunctionalExplained)
+* [Google Guava FluentIterable](https://github.com/google/guava/wiki/FunctionalExplained)
* [JOOQ](http://www.jooq.org/doc/3.0/manual/getting-started/use-cases/jooq-as-a-standalone-sql-builder/)
* [Mockito](http://mockito.org/)
* [Java Hamcrest](http://code.google.com/p/hamcrest/wiki/Tutorial)
diff --git a/fluentinterface/pom.xml b/fluentinterface/pom.xml
index 9eb063c13..138dbfa12 100644
--- a/fluentinterface/pom.xml
+++ b/fluentinterface/pom.xml
@@ -29,7 +29,7 @@
java-design-patterns
com.iluwatar
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
4.0.0
diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java
index 547c657e4..0222fc54a 100644
--- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java
+++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java
@@ -23,8 +23,6 @@
package com.iluwatar.fluentinterface.app;
-import static java.lang.String.valueOf;
-
import com.iluwatar.fluentinterface.fluentiterable.FluentIterable;
import com.iluwatar.fluentinterface.fluentiterable.lazy.LazyFluentIterable;
import com.iluwatar.fluentinterface.fluentiterable.simple.SimpleFluentIterable;
@@ -94,7 +92,7 @@ public class App {
.filter(positives())
.first(4)
.last(2)
- .map(number -> "String[" + valueOf(number) + "]")
+ .map(number -> "String[" + number + "]")
.asList();
prettyPrint("The lazy list contains the last two of the first four positive numbers "
+ "mapped to Strings: ", lastTwoOfFirstFourStringMapped);
diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java
index f001c532f..517e6b778 100644
--- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java
+++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java
@@ -70,7 +70,7 @@ public class LazyFluentIterable implements FluentIterable {
return new LazyFluentIterable<>() {
@Override
public Iterator iterator() {
- return new DecoratingIterator(iterable.iterator()) {
+ return new DecoratingIterator<>(iterable.iterator()) {
@Override
public E computeNext() {
while (fromIterator.hasNext()) {
@@ -107,10 +107,10 @@ public class LazyFluentIterable implements FluentIterable {
*/
@Override
public FluentIterable first(int count) {
- return new LazyFluentIterable() {
+ return new LazyFluentIterable<>() {
@Override
public Iterator iterator() {
- return new DecoratingIterator(iterable.iterator()) {
+ return new DecoratingIterator<>(iterable.iterator()) {
int currentIndex;
@Override
@@ -149,10 +149,10 @@ public class LazyFluentIterable implements FluentIterable {
*/
@Override
public FluentIterable last(int count) {
- return new LazyFluentIterable() {
+ return new LazyFluentIterable<>() {
@Override
public Iterator iterator() {
- return new DecoratingIterator(iterable.iterator()) {
+ return new DecoratingIterator<>(iterable.iterator()) {
private int stopIndex;
private int totalElementsCount;
private List list;
@@ -194,11 +194,11 @@ public class LazyFluentIterable implements FluentIterable {
*/
@Override
public FluentIterable map(Function super E, T> function) {
- return new LazyFluentIterable() {
+ return new LazyFluentIterable<>() {
@Override
public Iterator iterator() {
- return new DecoratingIterator(null) {
- Iterator oldTypeIterator = iterable.iterator();
+ return new DecoratingIterator<>(null) {
+ final Iterator oldTypeIterator = iterable.iterator();
@Override
public T computeNext() {
@@ -226,7 +226,7 @@ public class LazyFluentIterable implements FluentIterable {
@Override
public Iterator iterator() {
- return new DecoratingIterator(iterable.iterator()) {
+ return new DecoratingIterator<>(iterable.iterator()) {
@Override
public E computeNext() {
return fromIterator.hasNext() ? fromIterator.next() : null;
diff --git a/fluentinterface/src/test/java/com/iluwatar/fluentinterface/app/AppTest.java b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/app/AppTest.java
index 6f25d8416..1b6671917 100644
--- a/fluentinterface/src/test/java/com/iluwatar/fluentinterface/app/AppTest.java
+++ b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/app/AppTest.java
@@ -25,13 +25,15 @@ package com.iluwatar.fluentinterface.app;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Application Test Entry
*/
-public class AppTest {
+class AppTest {
@Test
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/flux/pom.xml b/flux/pom.xml
index 8effd0fc9..14f4f5557 100644
--- a/flux/pom.xml
+++ b/flux/pom.xml
@@ -29,7 +29,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
flux
diff --git a/flux/src/main/java/com/iluwatar/flux/action/Action.java b/flux/src/main/java/com/iluwatar/flux/action/Action.java
index 6a5f608c2..c8e2e012b 100644
--- a/flux/src/main/java/com/iluwatar/flux/action/Action.java
+++ b/flux/src/main/java/com/iluwatar/flux/action/Action.java
@@ -28,7 +28,7 @@ package com.iluwatar.flux.action;
*/
public abstract class Action {
- private ActionType type;
+ private final ActionType type;
public Action(ActionType type) {
this.type = type;
diff --git a/flux/src/main/java/com/iluwatar/flux/action/ActionType.java b/flux/src/main/java/com/iluwatar/flux/action/ActionType.java
index 6399d2806..e84954efd 100644
--- a/flux/src/main/java/com/iluwatar/flux/action/ActionType.java
+++ b/flux/src/main/java/com/iluwatar/flux/action/ActionType.java
@@ -28,6 +28,6 @@ package com.iluwatar.flux.action;
*/
public enum ActionType {
- MENU_ITEM_SELECTED, CONTENT_CHANGED;
+ MENU_ITEM_SELECTED, CONTENT_CHANGED
}
diff --git a/flux/src/main/java/com/iluwatar/flux/action/Content.java b/flux/src/main/java/com/iluwatar/flux/action/Content.java
index 59a63ec18..6fb2e3e0e 100644
--- a/flux/src/main/java/com/iluwatar/flux/action/Content.java
+++ b/flux/src/main/java/com/iluwatar/flux/action/Content.java
@@ -31,7 +31,7 @@ public enum Content {
PRODUCTS("Products - This page lists the company's products."), COMPANY(
"Company - This page displays information about the company.");
- private String title;
+ private final String title;
Content(String title) {
this.title = title;
diff --git a/flux/src/main/java/com/iluwatar/flux/action/ContentAction.java b/flux/src/main/java/com/iluwatar/flux/action/ContentAction.java
index 3b29b6b4e..c70561a65 100644
--- a/flux/src/main/java/com/iluwatar/flux/action/ContentAction.java
+++ b/flux/src/main/java/com/iluwatar/flux/action/ContentAction.java
@@ -28,7 +28,7 @@ package com.iluwatar.flux.action;
*/
public class ContentAction extends Action {
- private Content content;
+ private final Content content;
public ContentAction(Content content) {
super(ActionType.CONTENT_CHANGED);
diff --git a/flux/src/main/java/com/iluwatar/flux/action/MenuAction.java b/flux/src/main/java/com/iluwatar/flux/action/MenuAction.java
index 5ddeefde4..f833a6187 100644
--- a/flux/src/main/java/com/iluwatar/flux/action/MenuAction.java
+++ b/flux/src/main/java/com/iluwatar/flux/action/MenuAction.java
@@ -29,7 +29,7 @@ package com.iluwatar.flux.action;
*/
public class MenuAction extends Action {
- private MenuItem menuItem;
+ private final MenuItem menuItem;
public MenuAction(MenuItem menuItem) {
super(ActionType.MENU_ITEM_SELECTED);
diff --git a/flux/src/main/java/com/iluwatar/flux/action/MenuItem.java b/flux/src/main/java/com/iluwatar/flux/action/MenuItem.java
index f251e1dd7..90fac3e2e 100644
--- a/flux/src/main/java/com/iluwatar/flux/action/MenuItem.java
+++ b/flux/src/main/java/com/iluwatar/flux/action/MenuItem.java
@@ -30,7 +30,7 @@ public enum MenuItem {
HOME("Home"), PRODUCTS("Products"), COMPANY("Company");
- private String title;
+ private final String title;
MenuItem(String title) {
this.title = title;
diff --git a/flux/src/main/java/com/iluwatar/flux/dispatcher/Dispatcher.java b/flux/src/main/java/com/iluwatar/flux/dispatcher/Dispatcher.java
index cf09ecf68..c43d87680 100644
--- a/flux/src/main/java/com/iluwatar/flux/dispatcher/Dispatcher.java
+++ b/flux/src/main/java/com/iluwatar/flux/dispatcher/Dispatcher.java
@@ -39,7 +39,7 @@ public final class Dispatcher {
private static Dispatcher instance = new Dispatcher();
- private List stores = new LinkedList<>();
+ private final List stores = new LinkedList<>();
private Dispatcher() {
}
@@ -57,15 +57,10 @@ public final class Dispatcher {
*/
public void menuItemSelected(MenuItem menuItem) {
dispatchAction(new MenuAction(menuItem));
- switch (menuItem) {
- case HOME:
- case PRODUCTS:
- default:
- dispatchAction(new ContentAction(Content.PRODUCTS));
- break;
- case COMPANY:
- dispatchAction(new ContentAction(Content.COMPANY));
- break;
+ if (menuItem == MenuItem.COMPANY) {
+ dispatchAction(new ContentAction(Content.COMPANY));
+ } else {
+ dispatchAction(new ContentAction(Content.PRODUCTS));
}
}
diff --git a/flux/src/main/java/com/iluwatar/flux/store/Store.java b/flux/src/main/java/com/iluwatar/flux/store/Store.java
index cfbdf4af5..34188fff2 100644
--- a/flux/src/main/java/com/iluwatar/flux/store/Store.java
+++ b/flux/src/main/java/com/iluwatar/flux/store/Store.java
@@ -33,7 +33,7 @@ import java.util.List;
*/
public abstract class Store {
- private List views = new LinkedList<>();
+ private final List views = new LinkedList<>();
public abstract void onAction(Action action);
diff --git a/flux/src/test/java/com/iluwatar/flux/app/AppTest.java b/flux/src/test/java/com/iluwatar/flux/app/AppTest.java
index 8916ad4de..649708d8f 100644
--- a/flux/src/test/java/com/iluwatar/flux/app/AppTest.java
+++ b/flux/src/test/java/com/iluwatar/flux/app/AppTest.java
@@ -25,13 +25,15 @@ package com.iluwatar.flux.app;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Application test
*/
-public class AppTest {
+class AppTest {
@Test
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/flyweight/README.md b/flyweight/README.md
index 7b52ef800..fbefd3740 100644
--- a/flyweight/README.md
+++ b/flyweight/README.md
@@ -10,25 +10,32 @@ tags:
---
## Intent
-Use sharing to support large numbers of fine-grained objects
-efficiently.
+
+Use sharing to support large numbers of fine-grained objects efficiently.
## Explanation
+
Real world example
-> Alchemist's shop has shelves full of magic potions. Many of the potions are the same so there is no need to create new object for each of them. Instead one object instance can represent multiple shelf items so memory footprint remains small.
+> Alchemist's shop has shelves full of magic potions. Many of the potions are the same so there is
+> no need to create new object for each of them. Instead one object instance can represent multiple
+> shelf items so memory footprint remains small.
In plain words
-> It is used to minimize memory usage or computational expenses by sharing as much as possible with similar objects.
+> It is used to minimize memory usage or computational expenses by sharing as much as possible with
+> similar objects.
Wikipedia says
-> In computer programming, flyweight is a software design pattern. A flyweight is an object that minimizes memory use by sharing as much data as possible with other similar objects; it is a way to use objects in large numbers when a simple repeated representation would use an unacceptable amount of memory.
+> In computer programming, flyweight is a software design pattern. A flyweight is an object that
+> minimizes memory use by sharing as much data as possible with other similar objects; it is a way
+> to use objects in large numbers when a simple repeated representation would use an unacceptable
+> amount of memory.
**Programmatic example**
-Translating our alchemist shop example from above. First of all we have different potion types
+Translating our alchemist shop example from above. First of all we have different potion types:
```java
public interface Potion {
@@ -60,7 +67,7 @@ public class InvisibilityPotion implements Potion {
}
```
-Then the actual Flyweight object which is the factory for creating potions
+Then the actual Flyweight class `PotionFactory`, which is the factory for creating potions.
```java
public class PotionFactory {
@@ -96,7 +103,7 @@ public class PotionFactory {
}
```
-And it can be used as below
+And it can be used as below:
```java
var factory = new PotionFactory();
@@ -108,19 +115,33 @@ factory.createPotion(PotionType.HOLY_WATER).drink(); // You feel blessed. (Potio
factory.createPotion(PotionType.HEALING).drink(); // You feel healed. (Potion=648129364)
```
+Program output:
+
+```java
+You become invisible. (Potion=6566818)
+You feel healed. (Potion=648129364)
+You become invisible. (Potion=6566818)
+You feel blessed. (Potion=1104106489)
+You feel blessed. (Potion=1104106489)
+You feel healed. (Potion=648129364)
+```
+
## Class diagram
+

## Applicability
-The Flyweight pattern's effectiveness depends heavily on how
-and where it's used. Apply the Flyweight pattern when all of the following are
-true
-* an application uses a large number of objects
-* storage costs are high because of the sheer quantity of objects
-* most object state can be made extrinsic
-* many groups of objects may be replaced by relatively few shared objects once extrinsic state is removed
-* the application doesn't depend on object identity. Since flyweight objects may be shared, identity tests will return true for conceptually distinct objects.
+The Flyweight pattern's effectiveness depends heavily on how and where it's used. Apply the
+Flyweight pattern when all of the following are true:
+
+* An application uses a large number of objects.
+* Storage costs are high because of the sheer quantity of objects.
+* Most object state can be made extrinsic.
+* Many groups of objects may be replaced by relatively few shared objects once extrinsic state is
+removed.
+* The application doesn't depend on object identity. Since flyweight objects may be shared, identity
+tests will return true for conceptually distinct objects.
## Real world examples
diff --git a/flyweight/pom.xml b/flyweight/pom.xml
index f3a8082b5..b9ffd42ae 100644
--- a/flyweight/pom.xml
+++ b/flyweight/pom.xml
@@ -29,7 +29,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
flyweight
diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/AlchemistShop.java b/flyweight/src/main/java/com/iluwatar/flyweight/AlchemistShop.java
index 4fa7312e5..e7af8ee00 100644
--- a/flyweight/src/main/java/com/iluwatar/flyweight/AlchemistShop.java
+++ b/flyweight/src/main/java/com/iluwatar/flyweight/AlchemistShop.java
@@ -34,8 +34,8 @@ public class AlchemistShop {
private static final Logger LOGGER = LoggerFactory.getLogger(AlchemistShop.class);
- private List topShelf;
- private List bottomShelf;
+ private final List topShelf;
+ private final List bottomShelf;
/**
* Constructor.
diff --git a/flyweight/src/test/java/com/iluwatar/flyweight/AppTest.java b/flyweight/src/test/java/com/iluwatar/flyweight/AppTest.java
index 3d81a6db2..5f957c794 100644
--- a/flyweight/src/test/java/com/iluwatar/flyweight/AppTest.java
+++ b/flyweight/src/test/java/com/iluwatar/flyweight/AppTest.java
@@ -25,13 +25,15 @@ package com.iluwatar.flyweight;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Application test
*/
-public class AppTest {
+class AppTest {
@Test
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/front-controller/pom.xml b/front-controller/pom.xml
index 34dabc182..a90029a82 100644
--- a/front-controller/pom.xml
+++ b/front-controller/pom.xml
@@ -30,7 +30,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
front-controller
diff --git a/front-controller/src/test/java/com/iluwatar/front/controller/AppTest.java b/front-controller/src/test/java/com/iluwatar/front/controller/AppTest.java
index 2ea58c6a6..cf33bfb48 100644
--- a/front-controller/src/test/java/com/iluwatar/front/controller/AppTest.java
+++ b/front-controller/src/test/java/com/iluwatar/front/controller/AppTest.java
@@ -25,13 +25,15 @@ package com.iluwatar.front.controller;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Application test
*/
-public class AppTest {
+class AppTest {
@Test
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/front-controller/src/test/java/com/iluwatar/front/controller/utils/InMemoryAppender.java b/front-controller/src/test/java/com/iluwatar/front/controller/utils/InMemoryAppender.java
index 57cfb2454..8cbf7c631 100644
--- a/front-controller/src/test/java/com/iluwatar/front/controller/utils/InMemoryAppender.java
+++ b/front-controller/src/test/java/com/iluwatar/front/controller/utils/InMemoryAppender.java
@@ -36,7 +36,7 @@ import java.util.List;
*/
public class InMemoryAppender extends AppenderBase {
- private List log = new LinkedList<>();
+ private final List log = new LinkedList<>();
public InMemoryAppender() {
((Logger) LoggerFactory.getLogger("root")).addAppender(this);
diff --git a/game-loop/README.md b/game-loop/README.md
index 5f2cd9653..e967ff52d 100644
--- a/game-loop/README.md
+++ b/game-loop/README.md
@@ -8,31 +8,39 @@ tags:
- Game programming
---
-## Intent
-A game loop runs continuously during gameplay. Each turn of the loop, it processes user input without blocking, updates
-the game state, and renders the game. It tracks the passage of time to control the rate of gameplay.
+## Intent
+
+A game loop runs continuously during gameplay. Each turn of the loop, it processes user input
+without blocking, updates the game state, and renders the game. It tracks the passage of time to
+control the rate of gameplay.
This pattern decouples progression of game time from user input and processor speed.
-## Applicability
+## Applicability
+
This pattern is used in every game engine.
## Explanation
+
Real world example
-> Game loop is the main process of all the game rendering threads. It's present in all modern games. It drives input process, internal status update, rendering, AI and all the other processes.
+> Game loop is the main process of all the game rendering threads. It's present in all modern games.
+> It drives input process, internal status update, rendering, AI and all the other processes.
In plain words
-> Game Loop pattern ensures that game time progresses in equal speed in all different hardware setups.
+> Game Loop pattern ensures that game time progresses in equal speed in all different hardware
+> setups.
Wikipedia says
-> The central component of any game, from a programming standpoint, is the game loop. The game loop allows the game to run smoothly regardless of a user's input or lack thereof.
+> The central component of any game, from a programming standpoint, is the game loop. The game loop
+> allows the game to run smoothly regardless of a user's input, or lack thereof.
**Programmatic Example**
-Let's start with something simple. Here's a bullet that will move in our game. For demonstration it's enough that it has 1-dimensional position.
+Let's start with something simple. Here's `Bullet` class. Bullets will move in our game. For
+demonstration purposes it's enough that it has 1-dimensional position.
```java
public class Bullet {
@@ -53,7 +61,7 @@ public class Bullet {
}
```
-GameController is responsible for moving objects in the game. Including the aforementioned bullet.
+`GameController` is responsible for moving objects in the game, including the aforementioned bullet.
```java
public class GameController {
@@ -75,7 +83,8 @@ public class GameController {
}
```
-Now we introduce the game loop. Or actually in this demo we have 3 different game loops.
+Now we introduce the game loop. Or actually in this demo we have 3 different game loops. Let's see
+the base class `GameLoop` first.
```java
public enum GameStatus {
@@ -100,7 +109,7 @@ public abstract class GameLoop {
public void run() {
status = GameStatus.RUNNING;
- gameThread = new Thread(() -> processGameLoop());
+ gameThread = new Thread(this::processGameLoop);
gameThread.start();
}
@@ -128,7 +137,11 @@ public abstract class GameLoop {
protected abstract void processGameLoop();
}
+```
+Here's the first game loop implementation, `FrameBasedGameLoop`:
+
+```java
public class FrameBasedGameLoop extends GameLoop {
@Override
@@ -144,59 +157,9 @@ public class FrameBasedGameLoop extends GameLoop {
controller.moveBullet(0.5f);
}
}
-
-public class VariableStepGameLoop extends GameLoop {
-
- @Override
- protected void processGameLoop() {
- var lastFrameTime = System.currentTimeMillis();
- while (isGameRunning()) {
- processInput();
- var currentFrameTime = System.currentTimeMillis();
- var elapsedTime = currentFrameTime - lastFrameTime;
- update(elapsedTime);
- lastFrameTime = currentFrameTime;
- render();
- }
- }
-
- protected void update(Long elapsedTime) {
- controller.moveBullet(0.5f * elapsedTime / 1000);
- }
-}
-
-public class FixedStepGameLoop extends GameLoop {
-
- private static final long MS_PER_FRAME = 20;
-
- @Override
- protected void processGameLoop() {
- var previousTime = System.currentTimeMillis();
- var lag = 0L;
- while (isGameRunning()) {
- var currentTime = System.currentTimeMillis();
- var elapsedTime = currentTime - previousTime;
- previousTime = currentTime;
- lag += elapsedTime;
-
- processInput();
-
- while (lag >= MS_PER_FRAME) {
- update();
- lag -= MS_PER_FRAME;
- }
-
- render();
- }
- }
-
- protected void update() {
- controller.moveBullet(0.5f * MS_PER_FRAME / 1000);
- }
-}
```
-Finally we can show all these game loops in action.
+Finally, we show all the game loops in action.
```java
try {
@@ -226,10 +189,66 @@ Finally we can show all these game loops in action.
}
```
+Program output:
+
+```java
+Start frame-based game loop:
+Current bullet position: 0.5
+Current bullet position: 1.0
+Current bullet position: 1.5
+Current bullet position: 2.0
+Current bullet position: 2.5
+Current bullet position: 3.0
+Current bullet position: 3.5
+Current bullet position: 4.0
+Current bullet position: 4.5
+Current bullet position: 5.0
+Current bullet position: 5.5
+Current bullet position: 6.0
+Stop frame-based game loop.
+Start variable-step game loop:
+Current bullet position: 6.5
+Current bullet position: 0.038
+Current bullet position: 0.084
+Current bullet position: 0.145
+Current bullet position: 0.1805
+Current bullet position: 0.28
+Current bullet position: 0.32
+Current bullet position: 0.42549998
+Current bullet position: 0.52849996
+Current bullet position: 0.57799995
+Current bullet position: 0.63199997
+Current bullet position: 0.672
+Current bullet position: 0.778
+Current bullet position: 0.848
+Current bullet position: 0.8955
+Current bullet position: 0.9635
+Stop variable-step game loop.
+Start fixed-step game loop:
+Current bullet position: 0.0
+Current bullet position: 1.086
+Current bullet position: 0.059999995
+Current bullet position: 0.12999998
+Current bullet position: 0.24000004
+Current bullet position: 0.33999994
+Current bullet position: 0.36999992
+Current bullet position: 0.43999985
+Current bullet position: 0.5399998
+Current bullet position: 0.65999967
+Current bullet position: 0.68999964
+Current bullet position: 0.7299996
+Current bullet position: 0.79999954
+Current bullet position: 0.89999944
+Current bullet position: 0.98999935
+Stop variable-step game loop.
+```
+
## Class diagram
+

-## Credits
+## Credits
+
* [Game Programming Patterns - Game Loop](http://gameprogrammingpatterns.com/game-loop.html)
* [Game Programming Patterns](https://www.amazon.com/gp/product/0990582906/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0990582906&linkId=1289749a703b3fe0e24cd8d604d7c40b)
* [Game Engine Architecture, Third Edition](https://www.amazon.com/gp/product/1138035459/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1138035459&linkId=94502746617211bc40e0ef49d29333ac)
diff --git a/game-loop/pom.xml b/game-loop/pom.xml
index 2c2908271..6935c1fd1 100644
--- a/game-loop/pom.xml
+++ b/game-loop/pom.xml
@@ -29,7 +29,7 @@
java-design-patterns
com.iluwatar
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
4.0.0
@@ -39,6 +39,12 @@
junit
junit
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
diff --git a/game-loop/src/main/java/com/iluwatar/gameloop/GameLoop.java b/game-loop/src/main/java/com/iluwatar/gameloop/GameLoop.java
index cbb456ccf..5f47ef1dd 100644
--- a/game-loop/src/main/java/com/iluwatar/gameloop/GameLoop.java
+++ b/game-loop/src/main/java/com/iluwatar/gameloop/GameLoop.java
@@ -36,7 +36,7 @@ public abstract class GameLoop {
protected volatile GameStatus status;
- protected GameController controller;
+ protected final GameController controller;
private Thread gameThread;
@@ -53,7 +53,7 @@ public abstract class GameLoop {
*/
public void run() {
status = GameStatus.RUNNING;
- gameThread = new Thread(() -> processGameLoop());
+ gameThread = new Thread(this::processGameLoop);
gameThread.start();
}
diff --git a/game-loop/src/test/java/com/iluwatar/gameloop/AppTest.java b/game-loop/src/test/java/com/iluwatar/gameloop/AppTest.java
index 447e4f411..0c027028d 100644
--- a/game-loop/src/test/java/com/iluwatar/gameloop/AppTest.java
+++ b/game-loop/src/test/java/com/iluwatar/gameloop/AppTest.java
@@ -25,14 +25,16 @@ package com.iluwatar.gameloop;
import org.junit.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* App unit test class.
*/
public class AppTest {
@Test
- public void testMain() {
- new App().main(new String[]{});
+ public void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/guarded-suspension/pom.xml b/guarded-suspension/pom.xml
index 791c696c1..f1bd31c66 100644
--- a/guarded-suspension/pom.xml
+++ b/guarded-suspension/pom.xml
@@ -30,7 +30,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
jar
guarded-suspension
diff --git a/half-sync-half-async/pom.xml b/half-sync-half-async/pom.xml
index fdb37edb0..635b76172 100644
--- a/half-sync-half-async/pom.xml
+++ b/half-sync-half-async/pom.xml
@@ -29,7 +29,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
half-sync-half-async
diff --git a/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/App.java b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/App.java
index 7df2264ab..d013924cb 100644
--- a/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/App.java
+++ b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/App.java
@@ -95,7 +95,7 @@ public class App {
* ArithmeticSumTask.
*/
static class ArithmeticSumTask implements AsyncTask {
- private long numberOfElements;
+ private final long numberOfElements;
public ArithmeticSumTask(long numberOfElements) {
this.numberOfElements = numberOfElements;
diff --git a/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsynchronousService.java b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsynchronousService.java
index 3a3bb474c..32f5e9d4a 100644
--- a/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsynchronousService.java
+++ b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsynchronousService.java
@@ -48,7 +48,7 @@ public class AsynchronousService {
* tasks should be performed in the background which does not affect the performance of main
* thread.
*/
- private ExecutorService service;
+ private final ExecutorService service;
/**
* Creates an asynchronous service using {@code workQueue} as communication channel between
diff --git a/half-sync-half-async/src/test/java/com/iluwatar/halfsynchalfasync/AppTest.java b/half-sync-half-async/src/test/java/com/iluwatar/halfsynchalfasync/AppTest.java
index b3cf4839b..395599927 100644
--- a/half-sync-half-async/src/test/java/com/iluwatar/halfsynchalfasync/AppTest.java
+++ b/half-sync-half-async/src/test/java/com/iluwatar/halfsynchalfasync/AppTest.java
@@ -25,13 +25,15 @@ package com.iluwatar.halfsynchalfasync;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Application test
*/
-public class AppTest {
+class AppTest {
@Test
- public void test() {
- App.main(null);
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(null));
}
}
diff --git a/hexagonal/pom.xml b/hexagonal/pom.xml
index 4873d0ddb..f51825d5f 100644
--- a/hexagonal/pom.xml
+++ b/hexagonal/pom.xml
@@ -30,7 +30,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
hexagonal
diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/InMemoryBank.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/InMemoryBank.java
index 1a0fdb6b0..746b93508 100644
--- a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/InMemoryBank.java
+++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/InMemoryBank.java
@@ -32,7 +32,7 @@ import java.util.Map;
*/
public class InMemoryBank implements WireTransfers {
- private static Map accounts = new HashMap<>();
+ private static final Map accounts = new HashMap<>();
static {
accounts
diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/InMemoryTicketRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/InMemoryTicketRepository.java
index 973747acc..5c0461843 100644
--- a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/InMemoryTicketRepository.java
+++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/InMemoryTicketRepository.java
@@ -34,7 +34,7 @@ import java.util.Optional;
*/
public class InMemoryTicketRepository implements LotteryTicketRepository {
- private static Map tickets = new HashMap<>();
+ private static final Map tickets = new HashMap<>();
@Override
public Optional findById(LotteryTicketId id) {
diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java
index 96ab74ba3..6d6c33239 100644
--- a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java
+++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java
@@ -46,6 +46,7 @@ public class MongoTicketRepository implements LotteryTicketRepository {
private static final String DEFAULT_DB = "lotteryDB";
private static final String DEFAULT_TICKETS_COLLECTION = "lotteryTickets";
private static final String DEFAULT_COUNTERS_COLLECTION = "counters";
+ private static final String TICKET_ID = "ticketId";
private MongoClient mongoClient;
private MongoDatabase database;
@@ -93,7 +94,7 @@ public class MongoTicketRepository implements LotteryTicketRepository {
}
private void initCounters() {
- var doc = new Document("_id", "ticketId").append("seq", 1);
+ var doc = new Document("_id", TICKET_ID).append("seq", 1);
countersCollection.insertOne(doc);
}
@@ -103,7 +104,7 @@ public class MongoTicketRepository implements LotteryTicketRepository {
* @return next ticket id
*/
public int getNextId() {
- var find = new Document("_id", "ticketId");
+ var find = new Document("_id", TICKET_ID);
var increase = new Document("seq", 1);
var update = new Document("$inc", increase);
var result = countersCollection.findOneAndUpdate(find, update);
@@ -131,7 +132,7 @@ public class MongoTicketRepository implements LotteryTicketRepository {
@Override
public Optional findById(LotteryTicketId id) {
return ticketsCollection
- .find(new Document("ticketId", id.getId()))
+ .find(new Document(TICKET_ID, id.getId()))
.limit(1)
.into(new ArrayList<>())
.stream()
@@ -142,7 +143,7 @@ public class MongoTicketRepository implements LotteryTicketRepository {
@Override
public Optional save(LotteryTicket ticket) {
var ticketId = getNextId();
- var doc = new Document("ticketId", ticketId);
+ var doc = new Document(TICKET_ID, ticketId);
doc.put("email", ticket.getPlayerDetails().getEmail());
doc.put("bank", ticket.getPlayerDetails().getBankAccount());
doc.put("phone", ticket.getPlayerDetails().getPhoneNumber());
@@ -173,7 +174,7 @@ public class MongoTicketRepository implements LotteryTicketRepository {
.map(Integer::parseInt)
.collect(Collectors.toSet());
var lotteryNumbers = LotteryNumbers.create(numbers);
- var ticketId = new LotteryTicketId(doc.getInteger("ticketId"));
+ var ticketId = new LotteryTicketId(doc.getInteger(TICKET_ID));
return new LotteryTicket(ticketId, playerDetails, lotteryNumbers);
}
}
diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java
index 8988bba88..acdd2b8c5 100644
--- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java
+++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java
@@ -116,7 +116,7 @@ public class LotteryNumbers {
*/
private static class RandomNumberGenerator {
- private PrimitiveIterator.OfInt randomIterator;
+ private final PrimitiveIterator.OfInt randomIterator;
/**
* Initialize a new random number generator that generates random numbers in the range [min,
diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java
index dfa324449..114e78c9c 100644
--- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java
+++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java
@@ -30,7 +30,7 @@ import java.util.concurrent.atomic.AtomicInteger;
*/
public class LotteryTicketId {
- private static AtomicInteger numAllocated = new AtomicInteger(0);
+ private static final AtomicInteger numAllocated = new AtomicInteger(0);
private final int id;
public LotteryTicketId() {
diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/MongoEventLog.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/MongoEventLog.java
index ba46f2d97..62fcf32b4 100644
--- a/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/MongoEventLog.java
+++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/MongoEventLog.java
@@ -36,12 +36,15 @@ public class MongoEventLog implements LotteryEventLog {
private static final String DEFAULT_DB = "lotteryDB";
private static final String DEFAULT_EVENTS_COLLECTION = "events";
+ private static final String EMAIL = "email";
+ private static final String PHONE = "phone";
+ public static final String MESSAGE = "message";
private MongoClient mongoClient;
private MongoDatabase database;
private MongoCollection eventsCollection;
- private StdOutEventLog stdOutEventLog = new StdOutEventLog();
+ private final StdOutEventLog stdOutEventLog = new StdOutEventLog();
/**
* Constructor.
@@ -107,41 +110,41 @@ public class MongoEventLog implements LotteryEventLog {
@Override
public void ticketSubmitted(PlayerDetails details) {
- var document = new Document("email", details.getEmail());
- document.put("phone", details.getPhoneNumber());
+ var document = new Document(EMAIL, details.getEmail());
+ document.put(PHONE, details.getPhoneNumber());
document.put("bank", details.getBankAccount());
document
- .put("message", "Lottery ticket was submitted and bank account was charged for 3 credits.");
+ .put(MESSAGE, "Lottery ticket was submitted and bank account was charged for 3 credits.");
eventsCollection.insertOne(document);
stdOutEventLog.ticketSubmitted(details);
}
@Override
public void ticketSubmitError(PlayerDetails details) {
- var document = new Document("email", details.getEmail());
- document.put("phone", details.getPhoneNumber());
+ var document = new Document(EMAIL, details.getEmail());
+ document.put(PHONE, details.getPhoneNumber());
document.put("bank", details.getBankAccount());
- document.put("message", "Lottery ticket could not be submitted because lack of funds.");
+ document.put(MESSAGE, "Lottery ticket could not be submitted because lack of funds.");
eventsCollection.insertOne(document);
stdOutEventLog.ticketSubmitError(details);
}
@Override
public void ticketDidNotWin(PlayerDetails details) {
- var document = new Document("email", details.getEmail());
- document.put("phone", details.getPhoneNumber());
+ var document = new Document(EMAIL, details.getEmail());
+ document.put(PHONE, details.getPhoneNumber());
document.put("bank", details.getBankAccount());
- document.put("message", "Lottery ticket was checked and unfortunately did not win this time.");
+ document.put(MESSAGE, "Lottery ticket was checked and unfortunately did not win this time.");
eventsCollection.insertOne(document);
stdOutEventLog.ticketDidNotWin(details);
}
@Override
public void ticketWon(PlayerDetails details, int prizeAmount) {
- var document = new Document("email", details.getEmail());
- document.put("phone", details.getPhoneNumber());
+ var document = new Document(EMAIL, details.getEmail());
+ document.put(PHONE, details.getPhoneNumber());
document.put("bank", details.getBankAccount());
- document.put("message", String
+ document.put(MESSAGE, String
.format("Lottery ticket won! The bank account was deposited with %d credits.",
prizeAmount));
eventsCollection.insertOne(document);
@@ -150,10 +153,10 @@ public class MongoEventLog implements LotteryEventLog {
@Override
public void prizeError(PlayerDetails details, int prizeAmount) {
- var document = new Document("email", details.getEmail());
- document.put("phone", details.getPhoneNumber());
+ var document = new Document(EMAIL, details.getEmail());
+ document.put(PHONE, details.getPhoneNumber());
document.put("bank", details.getBankAccount());
- document.put("message", String
+ document.put(MESSAGE, String
.format("Lottery ticket won! Unfortunately the bank credit transfer of %d failed.",
prizeAmount));
eventsCollection.insertOne(document);
diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java
index 05d51a283..d2ad89c9b 100644
--- a/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java
+++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java
@@ -25,13 +25,16 @@ package com.iluwatar.hexagonal;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Unit test for simple App.
*/
class AppTest {
@Test
- void testApp() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
+
}
}
diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java
index 6d3ba8bc5..541b2b98b 100644
--- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java
+++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java
@@ -43,7 +43,7 @@ import org.junit.jupiter.api.Test;
*/
class LotteryTest {
- private Injector injector;
+ private final Injector injector;
@Inject
private LotteryAdministration administration;
@Inject
diff --git a/intercepting-filter/pom.xml b/intercepting-filter/pom.xml
index ea8597374..d8ee9985f 100644
--- a/intercepting-filter/pom.xml
+++ b/intercepting-filter/pom.xml
@@ -29,7 +29,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
intercepting-filter
diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Client.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Client.java
index 656008c10..52aa890c1 100644
--- a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Client.java
+++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Client.java
@@ -51,11 +51,11 @@ public class Client extends JFrame { // NOSONAR
private static final long serialVersionUID = 1L;
private transient FilterManager filterManager;
- private JLabel jl;
- private JTextField[] jtFields;
- private JTextArea[] jtAreas;
- private JButton clearButton;
- private JButton processButton;
+ private final JLabel jl;
+ private final JTextField[] jtFields;
+ private final JTextArea[] jtAreas;
+ private final JButton clearButton;
+ private final JButton processButton;
/**
* Constructor.
diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/FilterManager.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/FilterManager.java
index e8f3b941f..91e438882 100644
--- a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/FilterManager.java
+++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/FilterManager.java
@@ -30,7 +30,7 @@ package com.iluwatar.intercepting.filter;
*/
public class FilterManager {
- private FilterChain filterChain;
+ private final FilterChain filterChain;
public FilterManager() {
filterChain = new FilterChain();
diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Target.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Target.java
index 08ed715b1..db552356d 100644
--- a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Target.java
+++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Target.java
@@ -46,9 +46,9 @@ public class Target extends JFrame { //NOSONAR
private static final long serialVersionUID = 1L;
- private JTable jt;
- private DefaultTableModel dtm;
- private JButton del;
+ private final JTable jt;
+ private final DefaultTableModel dtm;
+ private final JButton del;
/**
* Constructor.
diff --git a/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/AppTest.java b/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/AppTest.java
index 8fbe9f1d2..d8f22a478 100644
--- a/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/AppTest.java
+++ b/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/AppTest.java
@@ -25,13 +25,15 @@ package com.iluwatar.intercepting.filter;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Application test.
*/
-public class AppTest {
+class AppTest {
@Test
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/interpreter/pom.xml b/interpreter/pom.xml
index 118cfcdf6..d32c80839 100644
--- a/interpreter/pom.xml
+++ b/interpreter/pom.xml
@@ -29,7 +29,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
interpreter
diff --git a/interpreter/src/main/java/com/iluwatar/interpreter/MinusExpression.java b/interpreter/src/main/java/com/iluwatar/interpreter/MinusExpression.java
index 24ef7914e..46b5c96cb 100644
--- a/interpreter/src/main/java/com/iluwatar/interpreter/MinusExpression.java
+++ b/interpreter/src/main/java/com/iluwatar/interpreter/MinusExpression.java
@@ -28,8 +28,8 @@ package com.iluwatar.interpreter;
*/
public class MinusExpression extends Expression {
- private Expression leftExpression;
- private Expression rightExpression;
+ private final Expression leftExpression;
+ private final Expression rightExpression;
public MinusExpression(Expression leftExpression, Expression rightExpression) {
this.leftExpression = leftExpression;
diff --git a/interpreter/src/main/java/com/iluwatar/interpreter/MultiplyExpression.java b/interpreter/src/main/java/com/iluwatar/interpreter/MultiplyExpression.java
index 606937e0b..926d6c119 100644
--- a/interpreter/src/main/java/com/iluwatar/interpreter/MultiplyExpression.java
+++ b/interpreter/src/main/java/com/iluwatar/interpreter/MultiplyExpression.java
@@ -28,8 +28,8 @@ package com.iluwatar.interpreter;
*/
public class MultiplyExpression extends Expression {
- private Expression leftExpression;
- private Expression rightExpression;
+ private final Expression leftExpression;
+ private final Expression rightExpression;
public MultiplyExpression(Expression leftExpression, Expression rightExpression) {
this.leftExpression = leftExpression;
diff --git a/interpreter/src/main/java/com/iluwatar/interpreter/NumberExpression.java b/interpreter/src/main/java/com/iluwatar/interpreter/NumberExpression.java
index 6b957f6aa..908eec8d1 100644
--- a/interpreter/src/main/java/com/iluwatar/interpreter/NumberExpression.java
+++ b/interpreter/src/main/java/com/iluwatar/interpreter/NumberExpression.java
@@ -28,7 +28,7 @@ package com.iluwatar.interpreter;
*/
public class NumberExpression extends Expression {
- private int number;
+ private final int number;
public NumberExpression(int number) {
this.number = number;
diff --git a/interpreter/src/main/java/com/iluwatar/interpreter/PlusExpression.java b/interpreter/src/main/java/com/iluwatar/interpreter/PlusExpression.java
index 1ce080259..38a8bb4af 100644
--- a/interpreter/src/main/java/com/iluwatar/interpreter/PlusExpression.java
+++ b/interpreter/src/main/java/com/iluwatar/interpreter/PlusExpression.java
@@ -28,8 +28,8 @@ package com.iluwatar.interpreter;
*/
public class PlusExpression extends Expression {
- private Expression leftExpression;
- private Expression rightExpression;
+ private final Expression leftExpression;
+ private final Expression rightExpression;
public PlusExpression(Expression leftExpression, Expression rightExpression) {
this.leftExpression = leftExpression;
diff --git a/interpreter/src/test/java/com/iluwatar/interpreter/AppTest.java b/interpreter/src/test/java/com/iluwatar/interpreter/AppTest.java
index 505dc559c..f4fb45637 100644
--- a/interpreter/src/test/java/com/iluwatar/interpreter/AppTest.java
+++ b/interpreter/src/test/java/com/iluwatar/interpreter/AppTest.java
@@ -25,13 +25,15 @@ package com.iluwatar.interpreter;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
/**
* Application test
*/
-public class AppTest {
+class AppTest {
@Test
- public void test() {
- App.main(new String[]{});
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
diff --git a/iterator/README.md b/iterator/README.md
index 7f06a64b9..71ad807cd 100644
--- a/iterator/README.md
+++ b/iterator/README.md
@@ -9,34 +9,39 @@ tags:
---
## Also known as
+
Cursor
## Intent
-Provide a way to access the elements of an aggregate object
-sequentially without exposing its underlying representation.
+Provide a way to access the elements of an aggregate object sequentially without exposing its
+underlying representation.
## Explanation
Real world example
-> Treasure chest contains a set of magical items. There multiple types of items such as rings, potions and weapons. The items can be browsed by type using an iterator the treasure chest provides.
+> Treasure chest contains a set of magical items. There multiple types of items such as rings,
+> potions and weapons. The items can be browsed by type using an iterator the treasure chest
+> provides.
In plain words
-> Containers can provide a representation agnostic iterator interface to provide access to the elements.
+> Containers can provide a representation agnostic iterator interface to provide access to the
+> elements.
Wikipedia says
-> In object-oriented programming, the iterator pattern is a design pattern in which an iterator is used to traverse a container and access the container's elements.
+> In object-oriented programming, the iterator pattern is a design pattern in which an iterator is
+> used to traverse a container and access the container's elements.
**Programmatic Example**
-The main class in our example is the treasure chest that contains items.
+The main class in our example is the `TreasureChest` that contains items.
```java
public class TreasureChest {
- private List- items;
+ private final List
- items;
public TreasureChest() {
items = List.of(
@@ -60,11 +65,15 @@ public class TreasureChest {
return new ArrayList<>(items);
}
}
+```
+Here's the `Item` class:
+
+```java
public class Item {
private ItemType type;
- private String name;
+ private final String name;
public Item(ItemType type, String name) {
this.setType(type);
@@ -92,7 +101,7 @@ public enum ItemType {
}
```
-The iterator interface is extremely simple.
+The `Iterator` interface is extremely simple.
```java
public interface Iterator {
@@ -110,19 +119,26 @@ var itemIterator = TREASURE_CHEST.iterator(ItemType.RING);
while (itemIterator.hasNext()) {
LOGGER.info(itemIterator.next().toString());
}
-// Ring of shadows
-// Ring of armor
+```
+
+Program output:
+
+```java
+Ring of shadows
+Ring of armor
```
## Class diagram
+

## Applicability
+
Use the Iterator pattern
-* to access an aggregate object's contents without exposing its internal representation
-* to support multiple traversals of aggregate objects
-* to provide a uniform interface for traversing different aggregate structures
+* To access an aggregate object's contents without exposing its internal representation.
+* To support multiple traversals of aggregate objects.
+* To provide a uniform interface for traversing different aggregate structures.
## Real world examples
diff --git a/iterator/pom.xml b/iterator/pom.xml
index 514cedbea..bca091f11 100644
--- a/iterator/pom.xml
+++ b/iterator/pom.xml
@@ -29,7 +29,7 @@
com.iluwatar
java-design-patterns
- 1.23.0-SNAPSHOT
+ 1.24.0-SNAPSHOT
iterator
diff --git a/iterator/src/main/java/com/iluwatar/iterator/App.java b/iterator/src/main/java/com/iluwatar/iterator/App.java
index b81e765be..053b5c4fb 100644
--- a/iterator/src/main/java/com/iluwatar/iterator/App.java
+++ b/iterator/src/main/java/com/iluwatar/iterator/App.java
@@ -62,7 +62,7 @@ public class App {
LOGGER.info("------------------------");
LOGGER.info("BST Iterator: ");
var root = buildIntegerBst();
- var bstIterator = new BstIterator(root);
+ var bstIterator = new BstIterator<>(root);
while (bstIterator.hasNext()) {
LOGGER.info("Next node: " + bstIterator.next().getVal());
}
diff --git a/iterator/src/main/java/com/iluwatar/iterator/bst/BstIterator.java b/iterator/src/main/java/com/iluwatar/iterator/bst/BstIterator.java
index b3e0dc3d6..9f584cddc 100644
--- a/iterator/src/main/java/com/iluwatar/iterator/bst/BstIterator.java
+++ b/iterator/src/main/java/com/iluwatar/iterator/bst/BstIterator.java
@@ -36,7 +36,7 @@ import java.util.NoSuchElementException;
*/
public class BstIterator> implements Iterator> {
- private ArrayDeque> pathStack;
+ private final ArrayDeque> pathStack;
public BstIterator(TreeNode root) {
pathStack = new ArrayDeque<>();
diff --git a/iterator/src/main/java/com/iluwatar/iterator/bst/README.md b/iterator/src/main/java/com/iluwatar/iterator/bst/README.md
index 816d1a4fe..07bcbf84f 100644
--- a/iterator/src/main/java/com/iluwatar/iterator/bst/README.md
+++ b/iterator/src/main/java/com/iluwatar/iterator/bst/README.md
@@ -1,8 +1,11 @@
# BSTIterator
-An implementation of the Iterator design pattern, for the Binary Search Tree
-data structure. A great explanation of BSTs can be found in this [video tutorial](https://www.youtube.com/watch?v=i_Q0v_Ct5lY).
-### What it Does
+An implementation of the Iterator design pattern, for the Binary Search Tree data structure. A great
+explanation of BSTs can be found in this
+[video tutorial](https://www.youtube.com/watch?v=i_Q0v_Ct5lY).
+
+### What It Does
+
This iterator assumes that the given binary search tree inserts nodes of smaller
value to the left, and nodes of larger value to the right of current node. Accordingly,
this iterator will return nodes according to "In Order" binary tree traversal.
@@ -12,6 +15,7 @@ return values in order: 1, 3, 4, 6, 7, 8, 10, 13, 14.

### How It's Done
+
**The trivial solution** to a binary search tree iterator would be to construct a List (or similar
linear data structure) when you construct the BSTIterator. This would require traversing the entire
BST, adding each node value to your list as you go. The downside to the trivial solution is twofold.
@@ -80,7 +84,4 @@ In Big O terms, here are the costs for our improved solution, where h is the hei
* Extra Space: O(h)
As you can see, this solution more evenly distributes the work. It yields the same amortized
-runtime for `next()`, reduces the run time of the constructor, and uses less extra space.
-
-
-
\ No newline at end of file
+runtime for `next()`, reduces the run time of the constructor, and uses less extra space.
diff --git a/iterator/src/main/java/com/iluwatar/iterator/bst/TreeNode.java b/iterator/src/main/java/com/iluwatar/iterator/bst/TreeNode.java
index 87f16e96c..b0ec5f486 100644
--- a/iterator/src/main/java/com/iluwatar/iterator/bst/TreeNode.java
+++ b/iterator/src/main/java/com/iluwatar/iterator/bst/TreeNode.java
@@ -31,7 +31,7 @@ package com.iluwatar.iterator.bst;
*/
public class TreeNode> {
- private T val;
+ private final T val;
private TreeNode