mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-10-13 22:16:03 +00:00
deps: Refactor dependencies (#3224)
* remove spring dep move junit, logging, mockito under dep mgmt * upgrade anti-corruption-layer deps * async method invocation * balking, bloc * bridge to bytecode * caching * callback - cqrs * component - health check * hexagonal - metadata mapping * rest of the patterns * remove checkstyle, take spotless into use
This commit is contained in:
1 parent
371439aeaa
commit
0ca162a55c
1863 files changed
+14403
-17632
No files matched your search
@@ -34,6 +34,14 @@
|
|||||||
</parent>
|
</parent>
|
||||||
<artifactId>abstract-document</artifactId>
|
<artifactId>abstract-document</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
|||||||
+7
-10
@@ -31,9 +31,7 @@ import java.util.Objects;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/** Abstract implementation of Document interface. */
|
||||||
* Abstract implementation of Document interface.
|
|
||||||
*/
|
|
||||||
public abstract class AbstractDocument implements Document {
|
public abstract class AbstractDocument implements Document {
|
||||||
|
|
||||||
private final Map<String, Object> documentProperties;
|
private final Map<String, Object> documentProperties;
|
||||||
@@ -57,12 +55,12 @@ public abstract class AbstractDocument implements Document {
|
|||||||
@Override
|
@Override
|
||||||
public <T> Stream<T> children(String key, Function<Map<String, Object>, T> childConstructor) {
|
public <T> Stream<T> children(String key, Function<Map<String, Object>, T> childConstructor) {
|
||||||
return Stream.ofNullable(get(key))
|
return Stream.ofNullable(get(key))
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.map(el -> (List<Map<String, Object>>) el)
|
.map(el -> (List<Map<String, Object>>) el)
|
||||||
.findAny()
|
.findAny()
|
||||||
.stream()
|
.stream()
|
||||||
.flatMap(Collection::stream)
|
.flatMap(Collection::stream)
|
||||||
.map(childConstructor);
|
.map(childConstructor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -100,5 +98,4 @@ public abstract class AbstractDocument implements Document {
|
|||||||
builder.append("]");
|
builder.append("]");
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -49,20 +49,26 @@ public class App {
|
|||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
LOGGER.info("Constructing parts and car");
|
LOGGER.info("Constructing parts and car");
|
||||||
|
|
||||||
var wheelProperties = Map.of(
|
var wheelProperties =
|
||||||
Property.TYPE.toString(), "wheel",
|
Map.of(
|
||||||
Property.MODEL.toString(), "15C",
|
Property.TYPE.toString(), "wheel",
|
||||||
Property.PRICE.toString(), 100L);
|
Property.MODEL.toString(), "15C",
|
||||||
|
Property.PRICE.toString(), 100L);
|
||||||
|
|
||||||
var doorProperties = Map.of(
|
var doorProperties =
|
||||||
Property.TYPE.toString(), "door",
|
Map.of(
|
||||||
Property.MODEL.toString(), "Lambo",
|
Property.TYPE.toString(), "door",
|
||||||
Property.PRICE.toString(), 300L);
|
Property.MODEL.toString(), "Lambo",
|
||||||
|
Property.PRICE.toString(), 300L);
|
||||||
|
|
||||||
var carProperties = Map.of(
|
var carProperties =
|
||||||
Property.MODEL.toString(), "300SL",
|
Map.of(
|
||||||
Property.PRICE.toString(), 10000L,
|
Property.MODEL.toString(),
|
||||||
Property.PARTS.toString(), List.of(wheelProperties, doorProperties));
|
"300SL",
|
||||||
|
Property.PRICE.toString(),
|
||||||
|
10000L,
|
||||||
|
Property.PARTS.toString(),
|
||||||
|
List.of(wheelProperties, doorProperties));
|
||||||
|
|
||||||
var car = new Car(carProperties);
|
var car = new Car(carProperties);
|
||||||
|
|
||||||
@@ -70,10 +76,13 @@ public class App {
|
|||||||
LOGGER.info("-> model: {}", car.getModel().orElseThrow());
|
LOGGER.info("-> model: {}", car.getModel().orElseThrow());
|
||||||
LOGGER.info("-> price: {}", car.getPrice().orElseThrow());
|
LOGGER.info("-> price: {}", car.getPrice().orElseThrow());
|
||||||
LOGGER.info("-> parts: ");
|
LOGGER.info("-> parts: ");
|
||||||
car.getParts().forEach(p -> LOGGER.info("\t{}/{}/{}",
|
car.getParts()
|
||||||
p.getType().orElse(null),
|
.forEach(
|
||||||
p.getModel().orElse(null),
|
p ->
|
||||||
p.getPrice().orElse(null))
|
LOGGER.info(
|
||||||
);
|
"\t{}/{}/{}",
|
||||||
|
p.getType().orElse(null),
|
||||||
|
p.getModel().orElse(null),
|
||||||
|
p.getPrice().orElse(null)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,15 +28,13 @@ import java.util.Map;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/** Document interface. */
|
||||||
* Document interface.
|
|
||||||
*/
|
|
||||||
public interface Document {
|
public interface Document {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts the value related to the key.
|
* Puts the value related to the key.
|
||||||
*
|
*
|
||||||
* @param key element key
|
* @param key element key
|
||||||
* @param value element value
|
* @param value element value
|
||||||
* @return Void
|
* @return Void
|
||||||
*/
|
*/
|
||||||
@@ -53,7 +51,7 @@ public interface Document {
|
|||||||
/**
|
/**
|
||||||
* Gets the stream of child documents.
|
* Gets the stream of child documents.
|
||||||
*
|
*
|
||||||
* @param key element key
|
* @param key element key
|
||||||
* @param constructor constructor of child class
|
* @param constructor constructor of child class
|
||||||
* @return child documents
|
* @return child documents
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -27,13 +27,10 @@ package com.iluwatar.abstractdocument.domain;
|
|||||||
import com.iluwatar.abstractdocument.AbstractDocument;
|
import com.iluwatar.abstractdocument.AbstractDocument;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/** Car entity. */
|
||||||
* Car entity.
|
|
||||||
*/
|
|
||||||
public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts {
|
public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts {
|
||||||
|
|
||||||
public Car(Map<String, Object> properties) {
|
public Car(Map<String, Object> properties) {
|
||||||
super(properties);
|
super(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -28,13 +28,10 @@ import com.iluwatar.abstractdocument.Document;
|
|||||||
import com.iluwatar.abstractdocument.domain.enums.Property;
|
import com.iluwatar.abstractdocument.domain.enums.Property;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/** HasModel trait for static access to 'model' property. */
|
||||||
* HasModel trait for static access to 'model' property.
|
|
||||||
*/
|
|
||||||
public interface HasModel extends Document {
|
public interface HasModel extends Document {
|
||||||
|
|
||||||
default Optional<String> getModel() {
|
default Optional<String> getModel() {
|
||||||
return Optional.ofNullable((String) get(Property.MODEL.toString()));
|
return Optional.ofNullable((String) get(Property.MODEL.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -28,13 +28,10 @@ import com.iluwatar.abstractdocument.Document;
|
|||||||
import com.iluwatar.abstractdocument.domain.enums.Property;
|
import com.iluwatar.abstractdocument.domain.enums.Property;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/** HasParts trait for static access to 'parts' property. */
|
||||||
* HasParts trait for static access to 'parts' property.
|
|
||||||
*/
|
|
||||||
public interface HasParts extends Document {
|
public interface HasParts extends Document {
|
||||||
|
|
||||||
default Stream<Part> getParts() {
|
default Stream<Part> getParts() {
|
||||||
return children(Property.PARTS.toString(), Part::new);
|
return children(Property.PARTS.toString(), Part::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -28,13 +28,10 @@ import com.iluwatar.abstractdocument.Document;
|
|||||||
import com.iluwatar.abstractdocument.domain.enums.Property;
|
import com.iluwatar.abstractdocument.domain.enums.Property;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/** HasPrice trait for static access to 'price' property. */
|
||||||
* HasPrice trait for static access to 'price' property.
|
|
||||||
*/
|
|
||||||
public interface HasPrice extends Document {
|
public interface HasPrice extends Document {
|
||||||
|
|
||||||
default Optional<Number> getPrice() {
|
default Optional<Number> getPrice() {
|
||||||
return Optional.ofNullable((Number) get(Property.PRICE.toString()));
|
return Optional.ofNullable((Number) get(Property.PRICE.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -28,13 +28,10 @@ import com.iluwatar.abstractdocument.Document;
|
|||||||
import com.iluwatar.abstractdocument.domain.enums.Property;
|
import com.iluwatar.abstractdocument.domain.enums.Property;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/** HasType trait for static access to 'type' property. */
|
||||||
* HasType trait for static access to 'type' property.
|
|
||||||
*/
|
|
||||||
public interface HasType extends Document {
|
public interface HasType extends Document {
|
||||||
|
|
||||||
default Optional<String> getType() {
|
default Optional<String> getType() {
|
||||||
return Optional.ofNullable((String) get(Property.TYPE.toString()));
|
return Optional.ofNullable((String) get(Property.TYPE.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -27,13 +27,10 @@ package com.iluwatar.abstractdocument.domain;
|
|||||||
import com.iluwatar.abstractdocument.AbstractDocument;
|
import com.iluwatar.abstractdocument.AbstractDocument;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/** Part entity. */
|
||||||
* Part entity.
|
|
||||||
*/
|
|
||||||
public class Part extends AbstractDocument implements HasType, HasModel, HasPrice {
|
public class Part extends AbstractDocument implements HasType, HasModel, HasPrice {
|
||||||
|
|
||||||
public Part(Map<String, Object> properties) {
|
public Part(Map<String, Object> properties) {
|
||||||
super(properties);
|
super(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+5
-5
@@ -24,10 +24,10 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.abstractdocument.domain.enums;
|
package com.iluwatar.abstractdocument.domain.enums;
|
||||||
|
|
||||||
/**
|
/** Enum To Describe Property type. */
|
||||||
* Enum To Describe Property type.
|
|
||||||
*/
|
|
||||||
public enum Property {
|
public enum Property {
|
||||||
|
PARTS,
|
||||||
PARTS, TYPE, PRICE, MODEL
|
TYPE,
|
||||||
|
PRICE,
|
||||||
|
MODEL
|
||||||
}
|
}
|
||||||
+14
-13
@@ -24,16 +24,14 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.abstractdocument;
|
package com.iluwatar.abstractdocument;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
/** AbstractDocument test class */
|
||||||
|
|
||||||
/**
|
|
||||||
* AbstractDocument test class
|
|
||||||
*/
|
|
||||||
class AbstractDocumentTest {
|
class AbstractDocumentTest {
|
||||||
|
|
||||||
private static final String KEY = "key";
|
private static final String KEY = "key";
|
||||||
@@ -82,13 +80,16 @@ class AbstractDocumentTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldHandleExceptionDuringConstruction() {
|
void shouldHandleExceptionDuringConstruction() {
|
||||||
Map<String, Object> invalidProperties = null; // Invalid properties, causing NullPointerException
|
Map<String, Object> invalidProperties =
|
||||||
|
null; // Invalid properties, causing NullPointerException
|
||||||
|
|
||||||
// Throw null pointer exception
|
// Throw null pointer exception
|
||||||
assertThrows(NullPointerException.class, () -> {
|
assertThrows(
|
||||||
// Attempt to construct a document with invalid properties
|
NullPointerException.class,
|
||||||
new DocumentImplementation(invalidProperties);
|
() -> {
|
||||||
});
|
// Attempt to construct a document with invalid properties
|
||||||
|
new DocumentImplementation(invalidProperties);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -97,11 +98,11 @@ class AbstractDocumentTest {
|
|||||||
DocumentImplementation nestedDocument = new DocumentImplementation(new HashMap<>());
|
DocumentImplementation nestedDocument = new DocumentImplementation(new HashMap<>());
|
||||||
nestedDocument.put("nestedKey", "nestedValue");
|
nestedDocument.put("nestedKey", "nestedValue");
|
||||||
|
|
||||||
|
|
||||||
document.put("nested", nestedDocument);
|
document.put("nested", nestedDocument);
|
||||||
|
|
||||||
// Retrieving the nested document
|
// Retrieving the nested document
|
||||||
DocumentImplementation retrievedNestedDocument = (DocumentImplementation) document.get("nested");
|
DocumentImplementation retrievedNestedDocument =
|
||||||
|
(DocumentImplementation) document.get("nested");
|
||||||
|
|
||||||
assertNotNull(retrievedNestedDocument);
|
assertNotNull(retrievedNestedDocument);
|
||||||
assertEquals("nestedValue", retrievedNestedDocument.get("nestedKey"));
|
assertEquals("nestedValue", retrievedNestedDocument.get("nestedKey"));
|
||||||
|
|||||||
@@ -24,25 +24,21 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.abstractdocument;
|
package com.iluwatar.abstractdocument;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
|
|
||||||
/**
|
import org.junit.jupiter.api.Test;
|
||||||
* Simple App test
|
|
||||||
*/
|
/** Simple App test */
|
||||||
class AppTest {
|
class AppTest {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Issue: Add at least one assertion to this test case.
|
* 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}
|
* <p>Solution: Inserted assertion to check whether the execution of the main method in {@link
|
||||||
* throws an exception.
|
* App} throws an exception.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldExecuteAppWithoutException() {
|
void shouldExecuteAppWithoutException() {
|
||||||
assertDoesNotThrow(() -> App.main(null));
|
assertDoesNotThrow(() -> App.main(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -24,18 +24,16 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.abstractdocument;
|
package com.iluwatar.abstractdocument;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
import com.iluwatar.abstractdocument.domain.Car;
|
import com.iluwatar.abstractdocument.domain.Car;
|
||||||
import com.iluwatar.abstractdocument.domain.Part;
|
import com.iluwatar.abstractdocument.domain.Part;
|
||||||
import com.iluwatar.abstractdocument.domain.enums.Property;
|
import com.iluwatar.abstractdocument.domain.enums.Property;
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
/** Test for Part and Car */
|
||||||
|
|
||||||
/**
|
|
||||||
* Test for Part and Car
|
|
||||||
*/
|
|
||||||
class DomainTest {
|
class DomainTest {
|
||||||
|
|
||||||
private static final String TEST_PART_TYPE = "test-part-type";
|
private static final String TEST_PART_TYPE = "test-part-type";
|
||||||
@@ -47,11 +45,11 @@ class DomainTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldConstructPart() {
|
void shouldConstructPart() {
|
||||||
var partProperties = Map.of(
|
var partProperties =
|
||||||
Property.TYPE.toString(), TEST_PART_TYPE,
|
Map.of(
|
||||||
Property.MODEL.toString(), TEST_PART_MODEL,
|
Property.TYPE.toString(), TEST_PART_TYPE,
|
||||||
Property.PRICE.toString(), (Object) TEST_PART_PRICE
|
Property.MODEL.toString(), TEST_PART_MODEL,
|
||||||
);
|
Property.PRICE.toString(), (Object) TEST_PART_PRICE);
|
||||||
var part = new Part(partProperties);
|
var part = new Part(partProperties);
|
||||||
assertEquals(TEST_PART_TYPE, part.getType().orElseThrow());
|
assertEquals(TEST_PART_TYPE, part.getType().orElseThrow());
|
||||||
assertEquals(TEST_PART_MODEL, part.getModel().orElseThrow());
|
assertEquals(TEST_PART_MODEL, part.getModel().orElseThrow());
|
||||||
@@ -60,15 +58,14 @@ class DomainTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldConstructCar() {
|
void shouldConstructCar() {
|
||||||
var carProperties = Map.of(
|
var carProperties =
|
||||||
Property.MODEL.toString(), TEST_CAR_MODEL,
|
Map.of(
|
||||||
Property.PRICE.toString(), TEST_CAR_PRICE,
|
Property.MODEL.toString(), TEST_CAR_MODEL,
|
||||||
Property.PARTS.toString(), List.of(Map.of(), Map.of())
|
Property.PRICE.toString(), TEST_CAR_PRICE,
|
||||||
);
|
Property.PARTS.toString(), List.of(Map.of(), Map.of()));
|
||||||
var car = new Car(carProperties);
|
var car = new Car(carProperties);
|
||||||
assertEquals(TEST_CAR_MODEL, car.getModel().orElseThrow());
|
assertEquals(TEST_CAR_MODEL, car.getModel().orElseThrow());
|
||||||
assertEquals(TEST_CAR_PRICE, car.getPrice().orElseThrow());
|
assertEquals(TEST_CAR_PRICE, car.getPrice().orElseThrow());
|
||||||
assertEquals(2, car.getParts().count());
|
assertEquals(2, car.getParts().count());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -34,6 +34,14 @@
|
|||||||
</parent>
|
</parent>
|
||||||
<artifactId>abstract-factory</artifactId>
|
<artifactId>abstract-factory</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ public class App implements Runnable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates kingdom.
|
* Creates kingdom.
|
||||||
|
*
|
||||||
* @param kingdomType type of Kingdom
|
* @param kingdomType type of Kingdom
|
||||||
*/
|
*/
|
||||||
public void createKingdom(final Kingdom.FactoryMaker.KingdomType kingdomType) {
|
public void createKingdom(final Kingdom.FactoryMaker.KingdomType kingdomType) {
|
||||||
@@ -82,4 +83,4 @@ public class App implements Runnable {
|
|||||||
kingdom.setCastle(kingdomFactory.createCastle());
|
kingdom.setCastle(kingdomFactory.createCastle());
|
||||||
kingdom.setArmy(kingdomFactory.createArmy());
|
kingdom.setArmy(kingdomFactory.createArmy());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,9 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.abstractfactory;
|
package com.iluwatar.abstractfactory;
|
||||||
|
|
||||||
/**
|
/** Army interface. */
|
||||||
* Army interface.
|
|
||||||
*/
|
|
||||||
public interface Army {
|
public interface Army {
|
||||||
|
|
||||||
String getDescription();
|
String getDescription();
|
||||||
|
|||||||
@@ -24,9 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.abstractfactory;
|
package com.iluwatar.abstractfactory;
|
||||||
|
|
||||||
/**
|
/** Castle interface. */
|
||||||
* Castle interface.
|
|
||||||
*/
|
|
||||||
public interface Castle {
|
public interface Castle {
|
||||||
|
|
||||||
String getDescription();
|
String getDescription();
|
||||||
|
|||||||
@@ -24,9 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.abstractfactory;
|
package com.iluwatar.abstractfactory;
|
||||||
|
|
||||||
/**
|
/** ElfArmy. */
|
||||||
* ElfArmy.
|
|
||||||
*/
|
|
||||||
public class ElfArmy implements Army {
|
public class ElfArmy implements Army {
|
||||||
|
|
||||||
static final String DESCRIPTION = "This is the elven army!";
|
static final String DESCRIPTION = "This is the elven army!";
|
||||||
|
|||||||
@@ -24,9 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.abstractfactory;
|
package com.iluwatar.abstractfactory;
|
||||||
|
|
||||||
/**
|
/** ElfCastle. */
|
||||||
* ElfCastle.
|
|
||||||
*/
|
|
||||||
public class ElfCastle implements Castle {
|
public class ElfCastle implements Castle {
|
||||||
|
|
||||||
static final String DESCRIPTION = "This is the elven castle!";
|
static final String DESCRIPTION = "This is the elven castle!";
|
||||||
|
|||||||
@@ -24,9 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.abstractfactory;
|
package com.iluwatar.abstractfactory;
|
||||||
|
|
||||||
/**
|
/** ElfKing. */
|
||||||
* ElfKing.
|
|
||||||
*/
|
|
||||||
public class ElfKing implements King {
|
public class ElfKing implements King {
|
||||||
|
|
||||||
static final String DESCRIPTION = "This is the elven king!";
|
static final String DESCRIPTION = "This is the elven king!";
|
||||||
|
|||||||
@@ -24,9 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.abstractfactory;
|
package com.iluwatar.abstractfactory;
|
||||||
|
|
||||||
/**
|
/** ElfKingdomFactory concrete factory. */
|
||||||
* ElfKingdomFactory concrete factory.
|
|
||||||
*/
|
|
||||||
public class ElfKingdomFactory implements KingdomFactory {
|
public class ElfKingdomFactory implements KingdomFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -43,5 +41,4 @@ public class ElfKingdomFactory implements KingdomFactory {
|
|||||||
public Army createArmy() {
|
public Army createArmy() {
|
||||||
return new ElfArmy();
|
return new ElfArmy();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -24,9 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.abstractfactory;
|
package com.iluwatar.abstractfactory;
|
||||||
|
|
||||||
/**
|
/** King interface. */
|
||||||
* King interface.
|
|
||||||
*/
|
|
||||||
public interface King {
|
public interface King {
|
||||||
|
|
||||||
String getDescription();
|
String getDescription();
|
||||||
|
|||||||
@@ -27,9 +27,7 @@ package com.iluwatar.abstractfactory;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
/**
|
/** Helper class to manufacture {@link KingdomFactory} beans. */
|
||||||
* Helper class to manufacture {@link KingdomFactory} beans.
|
|
||||||
*/
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public class Kingdom {
|
public class Kingdom {
|
||||||
@@ -38,21 +36,16 @@ public class Kingdom {
|
|||||||
private Castle castle;
|
private Castle castle;
|
||||||
private Army army;
|
private Army army;
|
||||||
|
|
||||||
/**
|
/** The factory of kingdom factories. */
|
||||||
* The factory of kingdom factories.
|
|
||||||
*/
|
|
||||||
public static class FactoryMaker {
|
public static class FactoryMaker {
|
||||||
|
|
||||||
/**
|
/** Enumeration for the different types of Kingdoms. */
|
||||||
* Enumeration for the different types of Kingdoms.
|
|
||||||
*/
|
|
||||||
public enum KingdomType {
|
public enum KingdomType {
|
||||||
ELF, ORC
|
ELF,
|
||||||
|
ORC
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** The factory method to create KingdomFactory concrete objects. */
|
||||||
* The factory method to create KingdomFactory concrete objects.
|
|
||||||
*/
|
|
||||||
public static KingdomFactory makeFactory(KingdomType type) {
|
public static KingdomFactory makeFactory(KingdomType type) {
|
||||||
return switch (type) {
|
return switch (type) {
|
||||||
case ELF -> new ElfKingdomFactory();
|
case ELF -> new ElfKingdomFactory();
|
||||||
|
|||||||
@@ -24,9 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.abstractfactory;
|
package com.iluwatar.abstractfactory;
|
||||||
|
|
||||||
/**
|
/** KingdomFactory factory interface. */
|
||||||
* KingdomFactory factory interface.
|
|
||||||
*/
|
|
||||||
public interface KingdomFactory {
|
public interface KingdomFactory {
|
||||||
|
|
||||||
Castle createCastle();
|
Castle createCastle();
|
||||||
@@ -34,5 +32,4 @@ public interface KingdomFactory {
|
|||||||
King createKing();
|
King createKing();
|
||||||
|
|
||||||
Army createArmy();
|
Army createArmy();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -24,9 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.abstractfactory;
|
package com.iluwatar.abstractfactory;
|
||||||
|
|
||||||
/**
|
/** OrcArmy. */
|
||||||
* OrcArmy.
|
|
||||||
*/
|
|
||||||
public class OrcArmy implements Army {
|
public class OrcArmy implements Army {
|
||||||
|
|
||||||
static final String DESCRIPTION = "This is the orc army!";
|
static final String DESCRIPTION = "This is the orc army!";
|
||||||
|
|||||||
@@ -24,9 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.abstractfactory;
|
package com.iluwatar.abstractfactory;
|
||||||
|
|
||||||
/**
|
/** OrcCastle. */
|
||||||
* OrcCastle.
|
|
||||||
*/
|
|
||||||
public class OrcCastle implements Castle {
|
public class OrcCastle implements Castle {
|
||||||
|
|
||||||
static final String DESCRIPTION = "This is the orc castle!";
|
static final String DESCRIPTION = "This is the orc castle!";
|
||||||
|
|||||||
@@ -24,9 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.abstractfactory;
|
package com.iluwatar.abstractfactory;
|
||||||
|
|
||||||
/**
|
/** OrcKing. */
|
||||||
* OrcKing.
|
|
||||||
*/
|
|
||||||
public class OrcKing implements King {
|
public class OrcKing implements King {
|
||||||
|
|
||||||
static final String DESCRIPTION = "This is the orc king!";
|
static final String DESCRIPTION = "This is the orc king!";
|
||||||
|
|||||||
@@ -24,9 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.abstractfactory;
|
package com.iluwatar.abstractfactory;
|
||||||
|
|
||||||
/**
|
/** OrcKingdomFactory concrete factory. */
|
||||||
* OrcKingdomFactory concrete factory.
|
|
||||||
*/
|
|
||||||
public class OrcKingdomFactory implements KingdomFactory {
|
public class OrcKingdomFactory implements KingdomFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+3
-5
@@ -24,14 +24,12 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.abstractfactory;
|
package com.iluwatar.abstractfactory;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
/**
|
import org.junit.jupiter.api.Test;
|
||||||
* Tests for abstract factory.
|
|
||||||
*/
|
/** Tests for abstract factory. */
|
||||||
class AbstractFactoryTest {
|
class AbstractFactoryTest {
|
||||||
|
|
||||||
private final App app = new App();
|
private final App app = new App();
|
||||||
|
|||||||
@@ -24,18 +24,16 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.abstractfactory;
|
package com.iluwatar.abstractfactory;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
|
|
||||||
/**
|
import org.junit.jupiter.api.Test;
|
||||||
* Check whether the execution of the main method in {@link App} throws an exception.
|
|
||||||
*/
|
/** Check whether the execution of the main method in {@link App} throws an exception. */
|
||||||
class AppTest {
|
class AppTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldExecuteApplicationWithoutException() {
|
void shouldExecuteApplicationWithoutException() {
|
||||||
|
|
||||||
assertDoesNotThrow(() -> App.main(new String[]{}));
|
assertDoesNotThrow(() -> App.main(new String[] {}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -34,6 +34,14 @@
|
|||||||
</parent>
|
</parent>
|
||||||
<artifactId>active-object</artifactId>
|
<artifactId>active-object</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
|||||||
@@ -29,86 +29,87 @@ import java.util.concurrent.LinkedBlockingQueue;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/** ActiveCreature class is the base of the active object example. */
|
||||||
* ActiveCreature class is the base of the active object example.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public abstract class ActiveCreature {
|
public abstract class ActiveCreature {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName());
|
private static final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName());
|
||||||
|
|
||||||
private BlockingQueue<Runnable> requests;
|
private BlockingQueue<Runnable> requests;
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
private Thread thread; // Thread of execution.
|
private Thread thread; // Thread of execution.
|
||||||
|
|
||||||
private int status; // status of the thread of execution.
|
private int status; // status of the thread of execution.
|
||||||
|
|
||||||
/**
|
/** Constructor and initialization. */
|
||||||
* Constructor and initialization.
|
|
||||||
*/
|
|
||||||
protected ActiveCreature(String name) {
|
protected ActiveCreature(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.status = 0;
|
this.status = 0;
|
||||||
this.requests = new LinkedBlockingQueue<>();
|
this.requests = new LinkedBlockingQueue<>();
|
||||||
thread = new Thread(() -> {
|
thread =
|
||||||
boolean infinite = true;
|
new Thread(
|
||||||
while (infinite) {
|
() -> {
|
||||||
try {
|
boolean infinite = true;
|
||||||
requests.take().run();
|
while (infinite) {
|
||||||
} catch (InterruptedException e) {
|
try {
|
||||||
if (this.status != 0) {
|
requests.take().run();
|
||||||
logger.error("Thread was interrupted. --> {}", e.getMessage());
|
} catch (InterruptedException e) {
|
||||||
}
|
if (this.status != 0) {
|
||||||
infinite = false;
|
logger.error("Thread was interrupted. --> {}", e.getMessage());
|
||||||
Thread.currentThread().interrupt();
|
}
|
||||||
}
|
infinite = false;
|
||||||
}
|
Thread.currentThread().interrupt();
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
thread.start();
|
thread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Eats the porridge.
|
* Eats the porridge.
|
||||||
|
*
|
||||||
* @throws InterruptedException due to firing a new Runnable.
|
* @throws InterruptedException due to firing a new Runnable.
|
||||||
*/
|
*/
|
||||||
public void eat() throws InterruptedException {
|
public void eat() throws InterruptedException {
|
||||||
requests.put(() -> {
|
requests.put(
|
||||||
logger.info("{} is eating!", name());
|
() -> {
|
||||||
logger.info("{} has finished eating!", name());
|
logger.info("{} is eating!", name());
|
||||||
});
|
logger.info("{} has finished eating!", name());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Roam the wastelands.
|
* Roam the wastelands.
|
||||||
|
*
|
||||||
* @throws InterruptedException due to firing a new Runnable.
|
* @throws InterruptedException due to firing a new Runnable.
|
||||||
*/
|
*/
|
||||||
public void roam() throws InterruptedException {
|
public void roam() throws InterruptedException {
|
||||||
requests.put(() ->
|
requests.put(() -> logger.info("{} has started to roam in the wastelands.", name()));
|
||||||
logger.info("{} has started to roam in the wastelands.", name())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name of the creature.
|
* Returns the name of the creature.
|
||||||
|
*
|
||||||
* @return the name of the creature.
|
* @return the name of the creature.
|
||||||
*/
|
*/
|
||||||
public String name() {
|
public String name() {
|
||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kills the thread of execution.
|
* Kills the thread of execution.
|
||||||
|
*
|
||||||
* @param status of the thread of execution. 0 == OK, the rest is logging an error.
|
* @param status of the thread of execution. 0 == OK, the rest is logging an error.
|
||||||
*/
|
*/
|
||||||
public void kill(int status) {
|
public void kill(int status) {
|
||||||
this.status = status;
|
this.status = status;
|
||||||
this.thread.interrupt();
|
this.thread.interrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the status of the thread of execution.
|
* Returns the status of the thread of execution.
|
||||||
|
*
|
||||||
* @return the status of the thread of execution.
|
* @return the status of the thread of execution.
|
||||||
*/
|
*/
|
||||||
public int getStatus() {
|
public int getStatus() {
|
||||||
|
|||||||
@@ -30,17 +30,17 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Active Object pattern helps to solve synchronization difficulties without using
|
* The Active Object pattern helps to solve synchronization difficulties without using
|
||||||
* 'synchronized' methods. The active object will contain a thread-safe data structure
|
* 'synchronized' methods. The active object will contain a thread-safe data structure (such as
|
||||||
* (such as BlockingQueue) and use to synchronize method calls by moving the logic of the method
|
* BlockingQueue) and use to synchronize method calls by moving the logic of the method into an
|
||||||
* into an invocator(usually a Runnable) and store it in the DSA.
|
* invocator(usually a Runnable) and store it in the DSA.
|
||||||
*
|
*
|
||||||
* <p>In this example, we fire 20 threads to modify a value in the target class.
|
* <p>In this example, we fire 20 threads to modify a value in the target class.
|
||||||
*/
|
*/
|
||||||
public class App implements Runnable {
|
public class App implements Runnable {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(App.class.getName());
|
private static final Logger logger = LoggerFactory.getLogger(App.class.getName());
|
||||||
|
|
||||||
private static final int NUM_CREATURES = 3;
|
private static final int NUM_CREATURES = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,11 +48,11 @@ public class App implements Runnable {
|
|||||||
*
|
*
|
||||||
* @param args command line arguments.
|
* @param args command line arguments.
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
var app = new App();
|
var app = new App();
|
||||||
app.run();
|
app.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
List<ActiveCreature> creatures = new ArrayList<>();
|
List<ActiveCreature> creatures = new ArrayList<>();
|
||||||
|
|||||||
@@ -24,14 +24,10 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.activeobject;
|
package com.iluwatar.activeobject;
|
||||||
|
|
||||||
/**
|
/** An implementation of the ActiveCreature class. */
|
||||||
* An implementation of the ActiveCreature class.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class Orc extends ActiveCreature {
|
public class Orc extends ActiveCreature {
|
||||||
|
|
||||||
public Orc(String name) {
|
public Orc(String name) {
|
||||||
super(name);
|
super(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -27,17 +27,16 @@ package com.iluwatar.activeobject;
|
|||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
class ActiveCreatureTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void executionTest() throws InterruptedException {
|
|
||||||
ActiveCreature orc = new Orc("orc1");
|
|
||||||
assertEquals("orc1",orc.name());
|
|
||||||
assertEquals(0,orc.getStatus());
|
|
||||||
orc.eat();
|
|
||||||
orc.roam();
|
|
||||||
orc.kill(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
class ActiveCreatureTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void executionTest() throws InterruptedException {
|
||||||
|
ActiveCreature orc = new Orc("orc1");
|
||||||
|
assertEquals("orc1", orc.name());
|
||||||
|
assertEquals(0, orc.getStatus());
|
||||||
|
orc.eat();
|
||||||
|
orc.roam();
|
||||||
|
orc.kill(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -28,11 +28,10 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
|||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
||||||
class AppTest {
|
class AppTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldExecuteApplicationWithoutException() {
|
void shouldExecuteApplicationWithoutException() {
|
||||||
assertDoesNotThrow(() -> App.main(new String[]{}));
|
assertDoesNotThrow(() -> App.main(new String[] {}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -34,6 +34,14 @@
|
|||||||
</parent>
|
</parent>
|
||||||
<artifactId>acyclic-visitor</artifactId>
|
<artifactId>acyclic-visitor</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
|||||||
@@ -28,6 +28,4 @@ package com.iluwatar.acyclicvisitor;
|
|||||||
* All ModemVisitor interface extends all visitor interfaces. This interface provides ease of use
|
* All ModemVisitor interface extends all visitor interfaces. This interface provides ease of use
|
||||||
* when a visitor needs to visit all modem types.
|
* when a visitor needs to visit all modem types.
|
||||||
*/
|
*/
|
||||||
public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {
|
public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {}
|
||||||
|
|
||||||
}
|
|
||||||
@@ -37,9 +37,7 @@ package com.iluwatar.acyclicvisitor;
|
|||||||
*/
|
*/
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
/**
|
/** Program's entry point. */
|
||||||
* Program's entry point.
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
var conUnix = new ConfigureForUnixVisitor();
|
var conUnix = new ConfigureForUnixVisitor();
|
||||||
var conDos = new ConfigureForDosVisitor();
|
var conDos = new ConfigureForDosVisitor();
|
||||||
@@ -50,6 +48,6 @@ public class App {
|
|||||||
hayes.accept(conDos); // Hayes modem with Dos configurator
|
hayes.accept(conDos); // Hayes modem with Dos configurator
|
||||||
zoom.accept(conDos); // Zoom modem with Dos configurator
|
zoom.accept(conDos); // Zoom modem with Dos configurator
|
||||||
hayes.accept(conUnix); // Hayes modem with Unix configurator
|
hayes.accept(conUnix); // Hayes modem with Unix configurator
|
||||||
zoom.accept(conUnix); // Zoom modem with Unix configurator
|
zoom.accept(conUnix); // Zoom modem with Unix configurator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-2
@@ -27,8 +27,7 @@ package com.iluwatar.acyclicvisitor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ConfigureForDosVisitor class implements both zoom's and hayes' visit method for Dos
|
* ConfigureForDosVisitor class implements both zoom's and hayes' visit method for Dos manufacturer.
|
||||||
* manufacturer.
|
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ConfigureForDosVisitor implements AllModemVisitor {
|
public class ConfigureForDosVisitor implements AllModemVisitor {
|
||||||
|
|||||||
+1
-1
@@ -37,4 +37,4 @@ public class ConfigureForUnixVisitor implements ZoomVisitor {
|
|||||||
public void visit(Zoom zoom) {
|
public void visit(Zoom zoom) {
|
||||||
LOGGER.info(zoom + " used with Unix configurator.");
|
LOGGER.info(zoom + " used with Unix configurator.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,15 +26,11 @@ package com.iluwatar.acyclicvisitor;
|
|||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
/**
|
/** Hayes class implements its accept method. */
|
||||||
* Hayes class implements its accept method.
|
|
||||||
*/
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class Hayes implements Modem {
|
public class Hayes implements Modem {
|
||||||
|
|
||||||
/**
|
/** Accepts all visitors but honors only HayesVisitor. */
|
||||||
* Accepts all visitors but honors only HayesVisitor.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void accept(ModemVisitor modemVisitor) {
|
public void accept(ModemVisitor modemVisitor) {
|
||||||
if (modemVisitor instanceof HayesVisitor) {
|
if (modemVisitor instanceof HayesVisitor) {
|
||||||
@@ -42,12 +38,9 @@ public class Hayes implements Modem {
|
|||||||
} else {
|
} else {
|
||||||
LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem");
|
LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Hayes' modem's toString method. */
|
||||||
* Hayes' modem's toString method.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Hayes modem";
|
return "Hayes modem";
|
||||||
|
|||||||
@@ -24,9 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.acyclicvisitor;
|
package com.iluwatar.acyclicvisitor;
|
||||||
|
|
||||||
/**
|
/** HayesVisitor interface. */
|
||||||
* HayesVisitor interface.
|
|
||||||
*/
|
|
||||||
public interface HayesVisitor extends ModemVisitor {
|
public interface HayesVisitor extends ModemVisitor {
|
||||||
void visit(Hayes hayes);
|
void visit(Hayes hayes);
|
||||||
}
|
}
|
||||||
@@ -24,10 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.acyclicvisitor;
|
package com.iluwatar.acyclicvisitor;
|
||||||
|
|
||||||
/**
|
/** //Modem abstract class. converted to an interface */
|
||||||
* //Modem abstract class.
|
|
||||||
* converted to an interface
|
|
||||||
*/
|
|
||||||
public interface Modem {
|
public interface Modem {
|
||||||
void accept(ModemVisitor modemVisitor);
|
void accept(ModemVisitor modemVisitor);
|
||||||
}
|
}
|
||||||
@@ -26,15 +26,11 @@ package com.iluwatar.acyclicvisitor;
|
|||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
/**
|
/** Zoom class implements its accept method. */
|
||||||
* Zoom class implements its accept method.
|
|
||||||
*/
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class Zoom implements Modem {
|
public class Zoom implements Modem {
|
||||||
|
|
||||||
/**
|
/** Accepts all visitors but honors only ZoomVisitor. */
|
||||||
* Accepts all visitors but honors only ZoomVisitor.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void accept(ModemVisitor modemVisitor) {
|
public void accept(ModemVisitor modemVisitor) {
|
||||||
if (modemVisitor instanceof ZoomVisitor) {
|
if (modemVisitor instanceof ZoomVisitor) {
|
||||||
@@ -44,9 +40,7 @@ public class Zoom implements Modem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Zoom modem's toString method. */
|
||||||
* Zoom modem's toString method.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Zoom modem";
|
return "Zoom modem";
|
||||||
|
|||||||
@@ -24,9 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.acyclicvisitor;
|
package com.iluwatar.acyclicvisitor;
|
||||||
|
|
||||||
/**
|
/** ZoomVisitor interface. */
|
||||||
* ZoomVisitor interface.
|
|
||||||
*/
|
|
||||||
public interface ZoomVisitor extends ModemVisitor {
|
public interface ZoomVisitor extends ModemVisitor {
|
||||||
void visit(Zoom zoom);
|
void visit(Zoom zoom);
|
||||||
}
|
}
|
||||||
@@ -24,25 +24,22 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.acyclicvisitor;
|
package com.iluwatar.acyclicvisitor;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
|
|
||||||
/**
|
import org.junit.jupiter.api.Test;
|
||||||
* Tests that the Acyclic Visitor example runs without errors.
|
|
||||||
*/
|
/** Tests that the Acyclic Visitor example runs without errors. */
|
||||||
class AppTest {
|
class AppTest {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Issue: Add at least one assertion to this test case.
|
* 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}
|
* <p>Solution: Inserted assertion to check whether the execution of the main method in {@link
|
||||||
* throws an exception.
|
* App} throws an exception.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldExecuteApplicationWithoutException() {
|
void shouldExecuteApplicationWithoutException() {
|
||||||
|
|
||||||
assertDoesNotThrow(() -> App.main(new String[]{}));
|
assertDoesNotThrow(() -> App.main(new String[] {}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,14 +24,12 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.acyclicvisitor;
|
package com.iluwatar.acyclicvisitor;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
/**
|
import org.junit.jupiter.api.Test;
|
||||||
* Hayes test class
|
|
||||||
*/
|
/** Hayes test class */
|
||||||
class HayesTest {
|
class HayesTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -24,16 +24,13 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.acyclicvisitor;
|
package com.iluwatar.acyclicvisitor;
|
||||||
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
/**
|
import org.junit.jupiter.api.Test;
|
||||||
* Zoom test class
|
|
||||||
*/
|
/** Zoom test class */
|
||||||
class ZoomTest {
|
class ZoomTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -34,6 +34,14 @@
|
|||||||
</parent>
|
</parent>
|
||||||
<artifactId>adapter</artifactId>
|
<artifactId>adapter</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
|||||||
@@ -37,16 +37,15 @@ package com.iluwatar.adapter;
|
|||||||
* <p>The Adapter ({@link FishingBoatAdapter}) converts the interface of the adaptee class ({@link
|
* <p>The Adapter ({@link FishingBoatAdapter}) converts the interface of the adaptee class ({@link
|
||||||
* FishingBoat}) into a suitable one expected by the client ({@link RowingBoat}).
|
* FishingBoat}) into a suitable one expected by the client ({@link RowingBoat}).
|
||||||
*
|
*
|
||||||
* <p>The story of this implementation is this. <br> Pirates are coming! we need a {@link
|
* <p>The story of this implementation is this. <br>
|
||||||
* RowingBoat} to flee! We have a {@link FishingBoat} and our captain. We have no time to make up a
|
* Pirates are coming! we need a {@link RowingBoat} to flee! We have a {@link FishingBoat} and our
|
||||||
* new ship! we need to reuse this {@link FishingBoat}. The captain needs a rowing boat which he can
|
* captain. We have no time to make up a new ship! we need to reuse this {@link FishingBoat}. The
|
||||||
* operate. The spec is in {@link RowingBoat}. We will use the Adapter pattern to reuse {@link
|
* captain needs a rowing boat which he can operate. The spec is in {@link RowingBoat}. We will use
|
||||||
* FishingBoat}.
|
* the Adapter pattern to reuse {@link FishingBoat}.
|
||||||
*/
|
*/
|
||||||
public final class App {
|
public final class App {
|
||||||
|
|
||||||
private App() {
|
private App() {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Program entry point.
|
* Program entry point.
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ import lombok.NoArgsConstructor;
|
|||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Captain uses {@link RowingBoat} to sail. <br> This is the client in the pattern.
|
* The Captain uses {@link RowingBoat} to sail. <br>
|
||||||
|
* This is the client in the pattern.
|
||||||
*/
|
*/
|
||||||
@Setter
|
@Setter
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@@ -41,5 +42,4 @@ public final class Captain {
|
|||||||
void row() {
|
void row() {
|
||||||
rowingBoat.row();
|
rowingBoat.row();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -36,5 +36,4 @@ final class FishingBoat {
|
|||||||
void sail() {
|
void sail() {
|
||||||
LOGGER.info("The fishing boat is sailing");
|
LOGGER.info("The fishing boat is sailing");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -25,10 +25,10 @@
|
|||||||
package com.iluwatar.adapter;
|
package com.iluwatar.adapter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The interface expected by the client.<br> A rowing boat is rowed to move.
|
* The interface expected by the client.<br>
|
||||||
|
* A rowing boat is rowed to move.
|
||||||
*/
|
*/
|
||||||
public interface RowingBoat {
|
public interface RowingBoat {
|
||||||
|
|
||||||
void row();
|
void row();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -24,17 +24,15 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.adapter;
|
package com.iluwatar.adapter;
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
/**
|
import java.util.HashMap;
|
||||||
* Tests for the adapter pattern.
|
import java.util.Map;
|
||||||
*/
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
/** Tests for the adapter pattern. */
|
||||||
class AdapterPatternTest {
|
class AdapterPatternTest {
|
||||||
|
|
||||||
private Map<String, Object> beans;
|
private Map<String, Object> beans;
|
||||||
@@ -43,9 +41,7 @@ class AdapterPatternTest {
|
|||||||
|
|
||||||
private static final String ROWING_BEAN = "captain";
|
private static final String ROWING_BEAN = "captain";
|
||||||
|
|
||||||
/**
|
/** This method runs before the test execution and sets the bean objects in the beans Map. */
|
||||||
* This method runs before the test execution and sets the bean objects in the beans Map.
|
|
||||||
*/
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setup() {
|
void setup() {
|
||||||
beans = new HashMap<>();
|
beans = new HashMap<>();
|
||||||
|
|||||||
@@ -24,23 +24,17 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.adapter;
|
package com.iluwatar.adapter;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
|
|
||||||
/**
|
import org.junit.jupiter.api.Test;
|
||||||
* Tests that Adapter example runs without errors.
|
|
||||||
*/
|
/** Tests that Adapter example runs without errors. */
|
||||||
class AppTest {
|
class AppTest {
|
||||||
|
|
||||||
/**
|
/** Check whether the execution of the main method in {@link App} throws an exception. */
|
||||||
* Check whether the execution of the main method in {@link App}
|
|
||||||
* throws an exception.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldExecuteApplicationWithoutException() {
|
void shouldExecuteApplicationWithoutException() {
|
||||||
|
|
||||||
assertDoesNotThrow(() -> App.main(new String[]{}));
|
assertDoesNotThrow(() -> App.main(new String[] {}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -34,6 +34,14 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>ambassador</artifactId>
|
<artifactId>ambassador</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ package com.iluwatar.ambassador;
|
|||||||
* The ambassador pattern creates a helper service that sends network requests on behalf of a
|
* The ambassador pattern creates a helper service that sends network requests on behalf of a
|
||||||
* client. It is often used in cloud-based applications to offload features of a remote service.
|
* client. It is often used in cloud-based applications to offload features of a remote service.
|
||||||
*
|
*
|
||||||
* <p>An ambassador service can be thought of as an out-of-process proxy that is co-located with
|
* <p>An ambassador service can be thought of as an out-of-process proxy that is co-located with the
|
||||||
* the client. Similar to the proxy design pattern, the ambassador service provides an interface for
|
* client. Similar to the proxy design pattern, the ambassador service provides an interface for
|
||||||
* another remote service. In addition to the interface, the ambassador provides extra functionality
|
* another remote service. In addition to the interface, the ambassador provides extra functionality
|
||||||
* and features, specifically offloaded common connectivity tasks. This usually consists of
|
* and features, specifically offloaded common connectivity tasks. This usually consists of
|
||||||
* monitoring, logging, routing, security etc. This is extremely useful in legacy applications where
|
* monitoring, logging, routing, security etc. This is extremely useful in legacy applications where
|
||||||
@@ -37,14 +37,11 @@ package com.iluwatar.ambassador;
|
|||||||
* capabilities.
|
* capabilities.
|
||||||
*
|
*
|
||||||
* <p>In this example, we will the ({@link ServiceAmbassador}) class represents the ambassador while
|
* <p>In this example, we will the ({@link ServiceAmbassador}) class represents the ambassador while
|
||||||
* the
|
* the ({@link RemoteService}) class represents a remote application.
|
||||||
* ({@link RemoteService}) class represents a remote application.
|
|
||||||
*/
|
*/
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
/**
|
/** Entry point. */
|
||||||
* Entry point.
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
var host1 = new Client();
|
var host1 = new Client();
|
||||||
var host2 = new Client();
|
var host2 = new Client();
|
||||||
|
|||||||
@@ -26,9 +26,7 @@ package com.iluwatar.ambassador;
|
|||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
/**
|
/** A simple Client. */
|
||||||
* A simple Client.
|
|
||||||
*/
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class Client {
|
public class Client {
|
||||||
|
|
||||||
|
|||||||
@@ -29,9 +29,7 @@ import static java.lang.Thread.sleep;
|
|||||||
import com.iluwatar.ambassador.util.RandomProvider;
|
import com.iluwatar.ambassador.util.RandomProvider;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
/**
|
/** A remote legacy application represented by a Singleton implementation. */
|
||||||
* A remote legacy application represented by a Singleton implementation.
|
|
||||||
*/
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class RemoteService implements RemoteServiceInterface {
|
public class RemoteService implements RemoteServiceInterface {
|
||||||
private static final int THRESHOLD = 200;
|
private static final int THRESHOLD = 200;
|
||||||
@@ -49,9 +47,7 @@ public class RemoteService implements RemoteServiceInterface {
|
|||||||
this(Math::random);
|
this(Math::random);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** This constructor is used for testing purposes only. */
|
||||||
* This constructor is used for testing purposes only.
|
|
||||||
*/
|
|
||||||
RemoteService(RandomProvider randomProvider) {
|
RemoteService(RandomProvider randomProvider) {
|
||||||
this.randomProvider = randomProvider;
|
this.randomProvider = randomProvider;
|
||||||
}
|
}
|
||||||
@@ -75,7 +71,8 @@ public class RemoteService implements RemoteServiceInterface {
|
|||||||
LOGGER.error("Thread sleep state interrupted", e);
|
LOGGER.error("Thread sleep state interrupted", e);
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
}
|
}
|
||||||
return waitTime <= THRESHOLD ? value * 10
|
return waitTime <= THRESHOLD
|
||||||
|
? value * 10
|
||||||
: RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue();
|
: RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,9 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.ambassador;
|
package com.iluwatar.ambassador;
|
||||||
|
|
||||||
/**
|
/** Interface shared by ({@link RemoteService}) and ({@link ServiceAmbassador}). */
|
||||||
* Interface shared by ({@link RemoteService}) and ({@link ServiceAmbassador}).
|
|
||||||
*/
|
|
||||||
interface RemoteServiceInterface {
|
interface RemoteServiceInterface {
|
||||||
|
|
||||||
long doRemoteFunction(int value);
|
long doRemoteFunction(int value);
|
||||||
|
|||||||
@@ -29,17 +29,14 @@ import lombok.Getter;
|
|||||||
/**
|
/**
|
||||||
* Holds information regarding the status of the Remote Service.
|
* Holds information regarding the status of the Remote Service.
|
||||||
*
|
*
|
||||||
* <p> This Enum replaces the integer value previously
|
* <p>This Enum replaces the integer value previously stored in {@link RemoteServiceInterface} as
|
||||||
* stored in {@link RemoteServiceInterface} as SonarCloud was identifying
|
* SonarCloud was identifying it as an issue. All test cases have been checked after changes,
|
||||||
* it as an issue. All test cases have been checked after changes,
|
* without failures.
|
||||||
* without failures. </p>
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public enum RemoteServiceStatus {
|
public enum RemoteServiceStatus {
|
||||||
FAILURE(-1);
|
FAILURE(-1);
|
||||||
|
|
||||||
@Getter
|
@Getter private final long remoteServiceStatusValue;
|
||||||
private final long remoteServiceStatusValue;
|
|
||||||
|
|
||||||
RemoteServiceStatus(long remoteServiceStatusValue) {
|
RemoteServiceStatus(long remoteServiceStatusValue) {
|
||||||
this.remoteServiceStatusValue = remoteServiceStatusValue;
|
this.remoteServiceStatusValue = remoteServiceStatusValue;
|
||||||
|
|||||||
@@ -40,8 +40,7 @@ public class ServiceAmbassador implements RemoteServiceInterface {
|
|||||||
private static final int RETRIES = 3;
|
private static final int RETRIES = 3;
|
||||||
private static final int DELAY_MS = 3000;
|
private static final int DELAY_MS = 3000;
|
||||||
|
|
||||||
ServiceAmbassador() {
|
ServiceAmbassador() {}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long doRemoteFunction(int value) {
|
public long doRemoteFunction(int value) {
|
||||||
|
|||||||
@@ -24,9 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.ambassador.util;
|
package com.iluwatar.ambassador.util;
|
||||||
|
|
||||||
/**
|
/** An interface for randomness. Useful for testing purposes. */
|
||||||
* An interface for randomness. Useful for testing purposes.
|
|
||||||
*/
|
|
||||||
public interface RandomProvider {
|
public interface RandomProvider {
|
||||||
double random();
|
double random();
|
||||||
}
|
}
|
||||||
@@ -24,25 +24,22 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.ambassador;
|
package com.iluwatar.ambassador;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
|
|
||||||
/**
|
import org.junit.jupiter.api.Test;
|
||||||
* Application test
|
|
||||||
*/
|
/** Application test */
|
||||||
class AppTest {
|
class AppTest {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Issue: Add at least one assertion to this test case.
|
* 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}
|
* <p>Solution: Inserted assertion to check whether the execution of the main method in {@link
|
||||||
* throws an exception.
|
* App} throws an exception.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldExecuteApplicationWithoutException() {
|
void shouldExecuteApplicationWithoutException() {
|
||||||
|
|
||||||
assertDoesNotThrow(() -> App.main(new String[]{}));
|
assertDoesNotThrow(() -> App.main(new String[] {}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,13 +24,11 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.ambassador;
|
package com.iluwatar.ambassador;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
/**
|
import org.junit.jupiter.api.Test;
|
||||||
* Test for {@link Client}
|
|
||||||
*/
|
/** Test for {@link Client} */
|
||||||
class ClientTest {
|
class ClientTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -38,6 +36,7 @@ class ClientTest {
|
|||||||
Client client = new Client();
|
Client client = new Client();
|
||||||
var result = client.useService(10);
|
var result = client.useService(10);
|
||||||
|
|
||||||
assertTrue(result == 100 || result == RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue());
|
assertTrue(
|
||||||
|
result == 100 || result == RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,9 +29,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
|||||||
import com.iluwatar.ambassador.util.RandomProvider;
|
import com.iluwatar.ambassador.util.RandomProvider;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
/**
|
/** Test for {@link RemoteService} */
|
||||||
* Test for {@link RemoteService}
|
|
||||||
*/
|
|
||||||
class RemoteServiceTest {
|
class RemoteServiceTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -24,18 +24,17 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.ambassador;
|
package com.iluwatar.ambassador;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
/**
|
import org.junit.jupiter.api.Test;
|
||||||
* Test for {@link ServiceAmbassador}
|
|
||||||
*/
|
/** Test for {@link ServiceAmbassador} */
|
||||||
class ServiceAmbassadorTest {
|
class ServiceAmbassadorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void test() {
|
void test() {
|
||||||
long result = new ServiceAmbassador().doRemoteFunction(10);
|
long result = new ServiceAmbassador().doRemoteFunction(10);
|
||||||
assertTrue(result == 100 || result == RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue());
|
assertTrue(
|
||||||
|
result == 100 || result == RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,8 +45,8 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|||||||
@@ -28,9 +28,8 @@ import org.springframework.boot.SpringApplication;
|
|||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This layer translates communications between the two systems,
|
* This layer translates communications between the two systems, allowing one system to remain
|
||||||
* allowing one system to remain unchanged while the other can avoid compromising
|
* unchanged while the other can avoid compromising its design and technological approach.
|
||||||
* its design and technological approach.
|
|
||||||
*/
|
*/
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
public class App {
|
public class App {
|
||||||
|
|||||||
@@ -23,30 +23,26 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Context and problem
|
* Context and problem Most applications rely on other systems for some data or functionality. For
|
||||||
* Most applications rely on other systems for some data or functionality.
|
* example, when a legacy application is migrated to a modern system, it may still need existing
|
||||||
* For example, when a legacy application is migrated to a modern system,
|
* legacy resources. New features must be able to call the legacy system. This is especially true of
|
||||||
* it may still need existing legacy resources. New features must be able to call the legacy system.
|
* gradual migrations, where different features of a larger application are moved to a modern system
|
||||||
* This is especially true of gradual migrations,
|
* over time.
|
||||||
* where different features of a larger application are moved to a modern system over time.
|
|
||||||
*
|
*
|
||||||
* <p>Often these legacy systems suffer from quality issues such as convoluted data schemas
|
* <p>Often these legacy systems suffer from quality issues such as convoluted data schemas or
|
||||||
* or obsolete APIs.
|
* obsolete APIs. The features and technologies used in legacy systems can vary widely from more
|
||||||
* The features and technologies used in legacy systems can vary widely from more modern systems.
|
* modern systems. To interoperate with the legacy system, the new application may need to support
|
||||||
* To interoperate with the legacy system,
|
* outdated infrastructure, protocols, data models, APIs, or other features that you wouldn't
|
||||||
* the new application may need to support outdated infrastructure, protocols, data models, APIs,
|
* otherwise put into a modern application.
|
||||||
* or other features that you wouldn't otherwise put into a modern application.
|
|
||||||
*
|
*
|
||||||
* <p>Maintaining access between new and legacy systems can force the new system to adhere to
|
* <p>Maintaining access between new and legacy systems can force the new system to adhere to at
|
||||||
* at least some of the legacy system's APIs or other semantics.
|
* least some of the legacy system's APIs or other semantics. When these legacy features have
|
||||||
* When these legacy features have quality issues, supporting them "corrupts" what might
|
* quality issues, supporting them "corrupts" what might otherwise be a cleanly designed modern
|
||||||
* otherwise be a cleanly designed modern application.
|
* application. Similar issues can arise with any external system that your development team doesn't
|
||||||
* Similar issues can arise with any external system that your development team doesn't control,
|
* control, not just legacy systems.
|
||||||
* not just legacy systems.
|
|
||||||
*
|
*
|
||||||
* <p>Solution Isolate the different subsystems by placing an anti-corruption layer between them.
|
* <p>Solution Isolate the different subsystems by placing an anti-corruption layer between them.
|
||||||
* This layer translates communications between the two systems,
|
* This layer translates communications between the two systems, allowing one system to remain
|
||||||
* allowing one system to remain unchanged while the other can avoid compromising
|
* unchanged while the other can avoid compromising its design and technological approach.
|
||||||
* its design and technological approach.
|
|
||||||
*/
|
*/
|
||||||
package com.iluwatar.corruption;
|
package com.iluwatar.corruption;
|
||||||
+16
-18
@@ -33,36 +33,34 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class represents an anti-corruption layer.
|
* The class represents an anti-corruption layer. The main purpose of the class is to provide a
|
||||||
* The main purpose of the class is to provide a layer between the modern and legacy systems.
|
* layer between the modern and legacy systems. The class is responsible for converting the data
|
||||||
* The class is responsible for converting the data from one system to another
|
* from one system to another decoupling the systems to each other
|
||||||
* decoupling the systems to each other
|
|
||||||
*
|
*
|
||||||
* <p>It allows using one system a domain model of the other system
|
* <p>It allows using one system a domain model of the other system without changing the domain
|
||||||
* without changing the domain model of the system.
|
* model of the system.
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class AntiCorruptionLayer {
|
public class AntiCorruptionLayer {
|
||||||
|
|
||||||
@Autowired
|
@Autowired private LegacyShop legacyShop;
|
||||||
private LegacyShop legacyShop;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The method converts the order from the legacy system to the modern system.
|
* The method converts the order from the legacy system to the modern system.
|
||||||
|
*
|
||||||
* @param id the id of the order
|
* @param id the id of the order
|
||||||
* @return the order in the modern system
|
* @return the order in the modern system
|
||||||
*/
|
*/
|
||||||
public Optional<ModernOrder> findOrderInLegacySystem(String id) {
|
public Optional<ModernOrder> findOrderInLegacySystem(String id) {
|
||||||
|
|
||||||
return legacyShop.findOrder(id).map(o ->
|
return legacyShop
|
||||||
new ModernOrder(
|
.findOrder(id)
|
||||||
o.getId(),
|
.map(
|
||||||
new Customer(o.getCustomer()),
|
o ->
|
||||||
new Shipment(o.getItem(), o.getQty(), o.getPrice()),
|
new ModernOrder(
|
||||||
""
|
o.getId(),
|
||||||
)
|
new Customer(o.getCustomer()),
|
||||||
);
|
new Shipment(o.getItem(), o.getQty(), o.getPrice()),
|
||||||
|
""));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -29,6 +29,7 @@ import java.util.Optional;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The class represents a data store for the modern system.
|
* The class represents a data store for the modern system.
|
||||||
|
*
|
||||||
* @param <V> the type of the value stored in the data store
|
* @param <V> the type of the value stored in the data store
|
||||||
*/
|
*/
|
||||||
public abstract class DataStore<V> {
|
public abstract class DataStore<V> {
|
||||||
@@ -44,6 +45,5 @@ public abstract class DataStore<V> {
|
|||||||
|
|
||||||
public Optional<V> put(String key, V value) {
|
public Optional<V> put(String key, V value) {
|
||||||
return Optional.ofNullable(inner.put(key, value));
|
return Optional.ofNullable(inner.put(key, value));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+8
-7
@@ -24,9 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.corruption.system;
|
package com.iluwatar.corruption.system;
|
||||||
|
|
||||||
/**
|
/** The class represents a general exception for the shop. */
|
||||||
* The class represents a general exception for the shop.
|
|
||||||
*/
|
|
||||||
public class ShopException extends Exception {
|
public class ShopException extends Exception {
|
||||||
public ShopException(String message) {
|
public ShopException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
@@ -41,9 +39,12 @@ public class ShopException extends Exception {
|
|||||||
* @throws ShopException the exception
|
* @throws ShopException the exception
|
||||||
*/
|
*/
|
||||||
public static ShopException throwIncorrectData(String lhs, String rhs) throws ShopException {
|
public static ShopException throwIncorrectData(String lhs, String rhs) throws ShopException {
|
||||||
throw new ShopException("The order is already placed but has an incorrect data:\n"
|
throw new ShopException(
|
||||||
+ "Incoming order: " + lhs + "\n"
|
"The order is already placed but has an incorrect data:\n"
|
||||||
+ "Existing order: " + rhs);
|
+ "Incoming order: "
|
||||||
|
+ lhs
|
||||||
|
+ "\n"
|
||||||
|
+ "Existing order: "
|
||||||
|
+ rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+2
-2
@@ -28,8 +28,8 @@ import lombok.AllArgsConstructor;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class represents an order in the legacy system.
|
* The class represents an order in the legacy system. The class is used by the legacy system to
|
||||||
* The class is used by the legacy system to store the data.
|
* store the data.
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
|
|||||||
+3
-5
@@ -28,10 +28,8 @@ import com.iluwatar.corruption.system.DataStore;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class represents a data store for the legacy system.
|
* The class represents a data store for the legacy system. The class is used by the legacy system
|
||||||
* The class is used by the legacy system to store the data.
|
* to store the data.
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class LegacyStore extends DataStore<LegacyOrder> {
|
public class LegacyStore extends DataStore<LegacyOrder> {}
|
||||||
}
|
|
||||||
|
|
||||||
+1
-3
@@ -27,9 +27,7 @@ package com.iluwatar.corruption.system.modern;
|
|||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/** The class represents a customer in the modern system. */
|
||||||
* The class represents a customer in the modern system.
|
|
||||||
*/
|
|
||||||
@Data
|
@Data
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class Customer {
|
public class Customer {
|
||||||
|
|||||||
+1
-5
@@ -27,9 +27,7 @@ package com.iluwatar.corruption.system.modern;
|
|||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/** The class represents an order in the modern system. */
|
||||||
* The class represents an order in the modern system.
|
|
||||||
*/
|
|
||||||
@Data
|
@Data
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class ModernOrder {
|
public class ModernOrder {
|
||||||
@@ -39,6 +37,4 @@ public class ModernOrder {
|
|||||||
private Shipment shipment;
|
private Shipment shipment;
|
||||||
|
|
||||||
private String extra;
|
private String extra;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
+7
-11
@@ -31,20 +31,18 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class represents a modern shop system.
|
* The class represents a modern shop system. The main purpose of the class is to place orders and
|
||||||
* The main purpose of the class is to place orders and find orders.
|
* find orders.
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class ModernShop {
|
public class ModernShop {
|
||||||
@Autowired
|
@Autowired private ModernStore store;
|
||||||
private ModernStore store;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired private AntiCorruptionLayer acl;
|
||||||
private AntiCorruptionLayer acl;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Places the order in the modern system.
|
* Places the order in the modern system. If the order is already present in the legacy system,
|
||||||
* If the order is already present in the legacy system, then no need to place it again.
|
* then no need to place it again.
|
||||||
*/
|
*/
|
||||||
public void placeOrder(ModernOrder order) throws ShopException {
|
public void placeOrder(ModernOrder order) throws ShopException {
|
||||||
|
|
||||||
@@ -62,9 +60,7 @@ public class ModernShop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Finds the order in the modern system. */
|
||||||
* Finds the order in the modern system.
|
|
||||||
*/
|
|
||||||
public Optional<ModernOrder> findOrder(String orderId) {
|
public Optional<ModernOrder> findOrder(String orderId) {
|
||||||
return store.get(orderId);
|
return store.get(orderId);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-6
@@ -27,10 +27,6 @@ package com.iluwatar.corruption.system.modern;
|
|||||||
import com.iluwatar.corruption.system.DataStore;
|
import com.iluwatar.corruption.system.DataStore;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
/**
|
/** The class represents a data store for the modern system. */
|
||||||
* The class represents a data store for the modern system.
|
|
||||||
*/
|
|
||||||
@Service
|
@Service
|
||||||
public class ModernStore extends DataStore<ModernOrder> {
|
public class ModernStore extends DataStore<ModernOrder> {}
|
||||||
}
|
|
||||||
|
|
||||||
+2
-2
@@ -28,8 +28,8 @@ import lombok.AllArgsConstructor;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class represents a shipment in the modern system.
|
* The class represents a shipment in the modern system. The class is used by the modern system to
|
||||||
* The class is used by the modern system to store the data.
|
* store the data.
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
|
|||||||
+53
-68
@@ -24,88 +24,73 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.corruption.system;
|
package com.iluwatar.corruption.system;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
import com.iluwatar.corruption.system.legacy.LegacyOrder;
|
import com.iluwatar.corruption.system.legacy.LegacyOrder;
|
||||||
import com.iluwatar.corruption.system.legacy.LegacyShop;
|
import com.iluwatar.corruption.system.legacy.LegacyShop;
|
||||||
import com.iluwatar.corruption.system.modern.Customer;
|
import com.iluwatar.corruption.system.modern.Customer;
|
||||||
import com.iluwatar.corruption.system.modern.ModernOrder;
|
import com.iluwatar.corruption.system.modern.ModernOrder;
|
||||||
import com.iluwatar.corruption.system.modern.ModernShop;
|
import com.iluwatar.corruption.system.modern.ModernShop;
|
||||||
import com.iluwatar.corruption.system.modern.Shipment;
|
import com.iluwatar.corruption.system.modern.Shipment;
|
||||||
import org.junit.Test;
|
import java.util.Optional;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.test.context.junit4.SpringRunner;
|
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
|
|
||||||
import java.util.Optional;
|
@ExtendWith(SpringExtension.class)
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
@RunWith(SpringRunner.class)
|
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
public class AntiCorruptionLayerTest {
|
public class AntiCorruptionLayerTest {
|
||||||
|
|
||||||
@Autowired
|
@Autowired private LegacyShop legacyShop;
|
||||||
private LegacyShop legacyShop;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired private ModernShop modernShop;
|
||||||
private ModernShop modernShop;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the anti-corruption layer. Main intention is to demonstrate how the anti-corruption layer
|
||||||
|
* works. The 2 shops (modern and legacy) should operate independently and in the same time
|
||||||
|
* synchronize the data.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void antiCorruptionLayerTest() throws ShopException {
|
||||||
|
// a new order comes to the legacy shop.
|
||||||
|
LegacyOrder legacyOrder = new LegacyOrder("1", "addr1", "item1", 1, 1);
|
||||||
|
// place the order in the legacy shop.
|
||||||
|
legacyShop.placeOrder(legacyOrder);
|
||||||
|
// the order is placed as usual since there is no other orders with the id in the both systems.
|
||||||
|
Optional<LegacyOrder> legacyOrderWithIdOne = legacyShop.findOrder("1");
|
||||||
|
assertEquals(Optional.of(legacyOrder), legacyOrderWithIdOne);
|
||||||
|
|
||||||
/**
|
// a new order (or maybe just the same order) appears in the modern shop
|
||||||
* Test the anti-corruption layer.
|
ModernOrder modernOrder =
|
||||||
* Main intention is to demonstrate how the anti-corruption layer works.
|
new ModernOrder("1", new Customer("addr1"), new Shipment("item1", 1, 1), "");
|
||||||
* <p>
|
// the system places it, but it checks if there is an order with the same id in the legacy shop.
|
||||||
* The 2 shops (modern and legacy) should operate independently and in the same time synchronize the data.
|
modernShop.placeOrder(modernOrder);
|
||||||
* To avoid corrupting the domain models of the 2 shops, we use an anti-corruption layer
|
|
||||||
* that transforms one model to another under the hood.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void antiCorruptionLayerTest() throws ShopException {
|
|
||||||
|
|
||||||
// a new order comes to the legacy shop.
|
Optional<ModernOrder> modernOrderWithIdOne = modernShop.findOrder("1");
|
||||||
LegacyOrder legacyOrder = new LegacyOrder("1", "addr1", "item1", 1, 1);
|
// there is no new order placed since there is already an order with the same id in the legacy
|
||||||
// place the order in the legacy shop.
|
// shop.
|
||||||
legacyShop.placeOrder(legacyOrder);
|
assertTrue(modernOrderWithIdOne.isEmpty());
|
||||||
// the order is placed as usual since there is no other orders with the id in the both systems.
|
}
|
||||||
Optional<LegacyOrder> legacyOrderWithIdOne = legacyShop.findOrder("1");
|
|
||||||
assertEquals(Optional.of(legacyOrder), legacyOrderWithIdOne);
|
|
||||||
|
|
||||||
// a new order (or maybe just the same order) appears in the modern shop.
|
/**
|
||||||
ModernOrder modernOrder = new ModernOrder("1", new Customer("addr1"), new Shipment("item1", 1, 1), "");
|
* Test the anti-corruption layer when a conflict occurs between systems. This test ensures that
|
||||||
|
* an exception is thrown when conflicting orders are placed.
|
||||||
// the system places it, but it checks if there is an order with the same id in the legacy shop.
|
*/
|
||||||
modernShop.placeOrder(modernOrder);
|
@Test
|
||||||
|
public void antiCorruptionLayerWithExTest() throws ShopException {
|
||||||
Optional<ModernOrder> modernOrderWithIdOne = modernShop.findOrder("1");
|
// a new order comes to the legacy shop.
|
||||||
// there is no new order placed since there is already an order with the same id in the legacy shop.
|
LegacyOrder legacyOrder = new LegacyOrder("1", "addr1", "item1", 1, 1);
|
||||||
assertTrue(modernOrderWithIdOne.isEmpty());
|
// place the order in the legacy shop.
|
||||||
|
legacyShop.placeOrder(legacyOrder);
|
||||||
}
|
// the order is placed as usual since there is no other orders with the id in the both systems.
|
||||||
/**
|
Optional<LegacyOrder> legacyOrderWithIdOne = legacyShop.findOrder("1");
|
||||||
* Test the anti-corruption layer.
|
assertEquals(Optional.of(legacyOrder), legacyOrderWithIdOne);
|
||||||
* Main intention is to demonstrate how the anti-corruption layer works.
|
// a new order but with the same id and different data appears in the modern shop
|
||||||
* <p>
|
ModernOrder modernOrder =
|
||||||
* This test tests the anti-corruption layer from the rule the orders should be the same in the both systems.
|
new ModernOrder("1", new Customer("addr1"), new Shipment("item1", 10, 1), "");
|
||||||
*
|
// the system rejects the order since there are 2 orders with contradiction there.
|
||||||
*/
|
assertThrows(ShopException.class, () -> modernShop.placeOrder(modernOrder));
|
||||||
@Test(expected = ShopException.class)
|
}
|
||||||
public void antiCorruptionLayerWithExTest() throws ShopException {
|
}
|
||||||
|
|
||||||
// a new order comes to the legacy shop.
|
|
||||||
LegacyOrder legacyOrder = new LegacyOrder("1", "addr1", "item1", 1, 1);
|
|
||||||
// place the order in the legacy shop.
|
|
||||||
legacyShop.placeOrder(legacyOrder);
|
|
||||||
// the order is placed as usual since there is no other orders with the id in the both systems.
|
|
||||||
Optional<LegacyOrder> legacyOrderWithIdOne = legacyShop.findOrder("1");
|
|
||||||
assertEquals(Optional.of(legacyOrder), legacyOrderWithIdOne);
|
|
||||||
|
|
||||||
// a new order but with the same id and different data appears in the modern shop
|
|
||||||
ModernOrder modernOrder = new ModernOrder("1", new Customer("addr1"), new Shipment("item1", 10, 1), "");
|
|
||||||
|
|
||||||
// the system rejects the order since there are 2 orders with contradiction there.
|
|
||||||
modernShop.placeOrder(modernOrder);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -35,12 +35,12 @@ public class Cash {
|
|||||||
|
|
||||||
private int amount;
|
private int amount;
|
||||||
|
|
||||||
//plus
|
// plus
|
||||||
void plus(int addend) {
|
void plus(int addend) {
|
||||||
amount += addend;
|
amount += addend;
|
||||||
}
|
}
|
||||||
|
|
||||||
//minus
|
// minus
|
||||||
boolean minus(int subtrahend) {
|
boolean minus(int subtrahend) {
|
||||||
if (amount >= subtrahend) {
|
if (amount >= subtrahend) {
|
||||||
amount -= subtrahend;
|
amount -= subtrahend;
|
||||||
@@ -50,7 +50,7 @@ public class Cash {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//count
|
// count
|
||||||
int count() {
|
int count() {
|
||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,8 +35,11 @@ import org.junit.jupiter.api.Test;
|
|||||||
* tests, so they're easier to read, maintain and enhance.
|
* tests, so they're easier to read, maintain and enhance.
|
||||||
*
|
*
|
||||||
* <p>It breaks tests down into three clear and distinct steps:
|
* <p>It breaks tests down into three clear and distinct steps:
|
||||||
|
*
|
||||||
* <p>1. Arrange: Perform the setup and initialization required for the test.
|
* <p>1. Arrange: Perform the setup and initialization required for the test.
|
||||||
|
*
|
||||||
* <p>2. Act: Take action(s) required for the test.
|
* <p>2. Act: Take action(s) required for the test.
|
||||||
|
*
|
||||||
* <p>3. Assert: Verify the outcome(s) of the test.
|
* <p>3. Assert: Verify the outcome(s) of the test.
|
||||||
*
|
*
|
||||||
* <p>This pattern has several significant benefits. It creates a clear separation between a test's
|
* <p>This pattern has several significant benefits. It creates a clear separation between a test's
|
||||||
@@ -48,53 +51,52 @@ import org.junit.jupiter.api.Test;
|
|||||||
* clearly about the three steps your test will perform. But it makes tests more natural to write at
|
* clearly about the three steps your test will perform. But it makes tests more natural to write at
|
||||||
* the same time since you already have an outline.
|
* the same time since you already have an outline.
|
||||||
*
|
*
|
||||||
* <p>In ({@link CashAAATest}) we have four test methods. Each of them has only one reason to
|
* <p>In ({@link CashAAATest}) we have four test methods. Each of them has only one reason to change
|
||||||
* change and one reason to fail. In a large and complicated code base, tests that honor the single
|
* and one reason to fail. In a large and complicated code base, tests that honor the single
|
||||||
* responsibility principle are much easier to troubleshoot.
|
* responsibility principle are much easier to troubleshoot.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class CashAAATest {
|
class CashAAATest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testPlus() {
|
void testPlus() {
|
||||||
//Arrange
|
// Arrange
|
||||||
var cash = new Cash(3);
|
var cash = new Cash(3);
|
||||||
//Act
|
// Act
|
||||||
cash.plus(4);
|
cash.plus(4);
|
||||||
//Assert
|
// Assert
|
||||||
assertEquals(7, cash.count());
|
assertEquals(7, cash.count());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testMinus() {
|
void testMinus() {
|
||||||
//Arrange
|
// Arrange
|
||||||
var cash = new Cash(8);
|
var cash = new Cash(8);
|
||||||
//Act
|
// Act
|
||||||
var result = cash.minus(5);
|
var result = cash.minus(5);
|
||||||
//Assert
|
// Assert
|
||||||
assertTrue(result);
|
assertTrue(result);
|
||||||
assertEquals(3, cash.count());
|
assertEquals(3, cash.count());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testInsufficientMinus() {
|
void testInsufficientMinus() {
|
||||||
//Arrange
|
// Arrange
|
||||||
var cash = new Cash(1);
|
var cash = new Cash(1);
|
||||||
//Act
|
// Act
|
||||||
var result = cash.minus(6);
|
var result = cash.minus(6);
|
||||||
//Assert
|
// Assert
|
||||||
assertFalse(result);
|
assertFalse(result);
|
||||||
assertEquals(1, cash.count());
|
assertEquals(1, cash.count());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testUpdate() {
|
void testUpdate() {
|
||||||
//Arrange
|
// Arrange
|
||||||
var cash = new Cash(5);
|
var cash = new Cash(5);
|
||||||
//Act
|
// Act
|
||||||
cash.plus(6);
|
cash.plus(6);
|
||||||
var result = cash.minus(3);
|
var result = cash.minus(3);
|
||||||
//Assert
|
// Assert
|
||||||
assertTrue(result);
|
assertTrue(result);
|
||||||
assertEquals(8, cash.count());
|
assertEquals(8, cash.count());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,23 +37,22 @@ import org.junit.jupiter.api.Test;
|
|||||||
* single responsibility principle. If this test method failed after a small code change, it might
|
* single responsibility principle. If this test method failed after a small code change, it might
|
||||||
* take some digging to discover why.
|
* take some digging to discover why.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class CashAntiAAATest {
|
class CashAntiAAATest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testCash() {
|
void testCash() {
|
||||||
//initialize
|
// initialize
|
||||||
var cash = new Cash(3);
|
var cash = new Cash(3);
|
||||||
//test plus
|
// test plus
|
||||||
cash.plus(4);
|
cash.plus(4);
|
||||||
assertEquals(7, cash.count());
|
assertEquals(7, cash.count());
|
||||||
//test minus
|
// test minus
|
||||||
cash = new Cash(8);
|
cash = new Cash(8);
|
||||||
assertTrue(cash.minus(5));
|
assertTrue(cash.minus(5));
|
||||||
assertEquals(3, cash.count());
|
assertEquals(3, cash.count());
|
||||||
assertFalse(cash.minus(6));
|
assertFalse(cash.minus(6));
|
||||||
assertEquals(3, cash.count());
|
assertEquals(3, cash.count());
|
||||||
//test update
|
// test update
|
||||||
cash.plus(5);
|
cash.plus(5);
|
||||||
assertTrue(cash.minus(5));
|
assertTrue(cash.minus(5));
|
||||||
assertEquals(3, cash.count());
|
assertEquals(3, cash.count());
|
||||||
|
|||||||
@@ -34,6 +34,14 @@
|
|||||||
</parent>
|
</parent>
|
||||||
<artifactId>async-method-invocation</artifactId>
|
<artifactId>async-method-invocation</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
|||||||
+12
-15
@@ -30,15 +30,15 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
/**
|
/**
|
||||||
* In this example, we are launching space rockets and deploying lunar rovers.
|
* In this example, we are launching space rockets and deploying lunar rovers.
|
||||||
*
|
*
|
||||||
* <p>The application demonstrates the async method invocation pattern. The key parts of the
|
* <p>The application demonstrates the async method invocation pattern. The key parts of the pattern
|
||||||
* pattern are <code>AsyncResult</code> which is an intermediate container for an asynchronously
|
* are <code>AsyncResult</code> which is an intermediate container for an asynchronously evaluated
|
||||||
* evaluated value, <code>AsyncCallback</code> which can be provided to be executed on task
|
* value, <code>AsyncCallback</code> which can be provided to be executed on task completion and
|
||||||
* completion and <code>AsyncExecutor</code> that manages the execution of the async tasks.
|
* <code>AsyncExecutor</code> that manages the execution of the async tasks.
|
||||||
*
|
*
|
||||||
* <p>The main method shows example flow of async invocations. The main thread starts multiple
|
* <p>The main method shows example flow of async invocations. The main thread starts multiple tasks
|
||||||
* tasks with variable durations and then continues its own work. When the main thread has done it's
|
* with variable durations and then continues its own work. When the main thread has done it's job
|
||||||
* job it collects the results of the async tasks. Two of the tasks are handled with callbacks,
|
* it collects the results of the async tasks. Two of the tasks are handled with callbacks, meaning
|
||||||
* meaning the callbacks are executed immediately when the tasks complete.
|
* the callbacks are executed immediately when the tasks complete.
|
||||||
*
|
*
|
||||||
* <p>Noteworthy difference of thread usage between the async results and callbacks is that the
|
* <p>Noteworthy difference of thread usage between the async results and callbacks is that the
|
||||||
* async results are collected in the main thread but the callbacks are executed within the worker
|
* async results are collected in the main thread but the callbacks are executed within the worker
|
||||||
@@ -62,10 +62,7 @@ public class App {
|
|||||||
|
|
||||||
private static final String ROCKET_LAUNCH_LOG_PATTERN = "Space rocket <%s> launched successfully";
|
private static final String ROCKET_LAUNCH_LOG_PATTERN = "Space rocket <%s> launched successfully";
|
||||||
|
|
||||||
/**
|
/** Program entry point. */
|
||||||
* Program entry point.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
// construct a new executor that will run async tasks
|
// construct a new executor that will run async tasks
|
||||||
var executor = new ThreadAsyncExecutor();
|
var executor = new ThreadAsyncExecutor();
|
||||||
@@ -74,8 +71,8 @@ public class App {
|
|||||||
final var asyncResult1 = executor.startProcess(lazyval(10, 500));
|
final var asyncResult1 = executor.startProcess(lazyval(10, 500));
|
||||||
final var asyncResult2 = executor.startProcess(lazyval("test", 300));
|
final var asyncResult2 = executor.startProcess(lazyval("test", 300));
|
||||||
final var asyncResult3 = executor.startProcess(lazyval(50L, 700));
|
final var asyncResult3 = executor.startProcess(lazyval(50L, 700));
|
||||||
final var asyncResult4 = executor.startProcess(lazyval(20, 400),
|
final var asyncResult4 =
|
||||||
callback("Deploying lunar rover"));
|
executor.startProcess(lazyval(20, 400), callback("Deploying lunar rover"));
|
||||||
final var asyncResult5 =
|
final var asyncResult5 =
|
||||||
executor.startProcess(lazyval("callback", 600), callback("Deploying lunar rover"));
|
executor.startProcess(lazyval("callback", 600), callback("Deploying lunar rover"));
|
||||||
|
|
||||||
@@ -99,7 +96,7 @@ public class App {
|
|||||||
/**
|
/**
|
||||||
* Creates a callable that lazily evaluates to given value with artificial delay.
|
* Creates a callable that lazily evaluates to given value with artificial delay.
|
||||||
*
|
*
|
||||||
* @param value value to evaluate
|
* @param value value to evaluate
|
||||||
* @param delayMillis artificial delay in milliseconds
|
* @param delayMillis artificial delay in milliseconds
|
||||||
* @return new callable for lazy evaluation
|
* @return new callable for lazy evaluation
|
||||||
*/
|
*/
|
||||||
|
|||||||
+3
-5
@@ -27,9 +27,7 @@ package com.iluwatar.async.method.invocation;
|
|||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
/**
|
/** AsyncExecutor interface. */
|
||||||
* AsyncExecutor interface.
|
|
||||||
*/
|
|
||||||
public interface AsyncExecutor {
|
public interface AsyncExecutor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -44,7 +42,7 @@ public interface AsyncExecutor {
|
|||||||
* Starts processing of an async task. Returns immediately with async result. Executes callback
|
* Starts processing of an async task. Returns immediately with async result. Executes callback
|
||||||
* when the task is completed.
|
* when the task is completed.
|
||||||
*
|
*
|
||||||
* @param task task to be executed asynchronously
|
* @param task task to be executed asynchronously
|
||||||
* @param callback callback to be executed on task completion
|
* @param callback callback to be executed on task completion
|
||||||
* @return async result for the task
|
* @return async result for the task
|
||||||
*/
|
*/
|
||||||
@@ -56,7 +54,7 @@ public interface AsyncExecutor {
|
|||||||
*
|
*
|
||||||
* @param asyncResult async result of a task
|
* @param asyncResult async result of a task
|
||||||
* @return evaluated value of the completed task
|
* @return evaluated value of the completed task
|
||||||
* @throws ExecutionException if execution has failed, containing the root cause
|
* @throws ExecutionException if execution has failed, containing the root cause
|
||||||
* @throws InterruptedException if the execution is interrupted
|
* @throws InterruptedException if the execution is interrupted
|
||||||
*/
|
*/
|
||||||
<T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException;
|
<T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException;
|
||||||
|
|||||||
+1
-1
@@ -44,7 +44,7 @@ public interface AsyncResult<T> {
|
|||||||
* Gets the value of completed async task.
|
* Gets the value of completed async task.
|
||||||
*
|
*
|
||||||
* @return evaluated value or throws ExecutionException if execution has failed
|
* @return evaluated value or throws ExecutionException if execution has failed
|
||||||
* @throws ExecutionException if execution has failed, containing the root cause
|
* @throws ExecutionException if execution has failed, containing the root cause
|
||||||
* @throws IllegalStateException if execution is not completed
|
* @throws IllegalStateException if execution is not completed
|
||||||
*/
|
*/
|
||||||
T getValue() throws ExecutionException;
|
T getValue() throws ExecutionException;
|
||||||
|
|||||||
+14
-16
@@ -24,19 +24,14 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.async.method.invocation;
|
package com.iluwatar.async.method.invocation;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
/**
|
/** Implementation of async executor that creates a new thread for every task. */
|
||||||
* Implementation of async executor that creates a new thread for every task.
|
|
||||||
*/
|
|
||||||
public class ThreadAsyncExecutor implements AsyncExecutor {
|
public class ThreadAsyncExecutor implements AsyncExecutor {
|
||||||
|
|
||||||
/**
|
/** Index for thread naming. */
|
||||||
* Index for thread naming.
|
|
||||||
*/
|
|
||||||
private final AtomicInteger idx = new AtomicInteger(0);
|
private final AtomicInteger idx = new AtomicInteger(0);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -47,19 +42,22 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
|
|||||||
@Override
|
@Override
|
||||||
public <T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback) {
|
public <T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback) {
|
||||||
var result = new CompletableResult<>(callback);
|
var result = new CompletableResult<>(callback);
|
||||||
new Thread(() -> {
|
new Thread(
|
||||||
try {
|
() -> {
|
||||||
result.setValue(task.call());
|
try {
|
||||||
} catch (Exception ex) {
|
result.setValue(task.call());
|
||||||
result.setException(ex);
|
} catch (Exception ex) {
|
||||||
}
|
result.setException(ex);
|
||||||
}, "executor-" + idx.incrementAndGet()).start();
|
}
|
||||||
|
},
|
||||||
|
"executor-" + idx.incrementAndGet())
|
||||||
|
.start();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException,
|
public <T> T endProcess(AsyncResult<T> asyncResult)
|
||||||
InterruptedException {
|
throws ExecutionException, InterruptedException {
|
||||||
if (!asyncResult.isCompleted()) {
|
if (!asyncResult.isCompleted()) {
|
||||||
asyncResult.await();
|
asyncResult.await();
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-10
@@ -24,26 +24,22 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.async.method.invocation;
|
package com.iluwatar.async.method.invocation;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
|
|
||||||
/**
|
import org.junit.jupiter.api.Test;
|
||||||
* Application test
|
|
||||||
*/
|
/** Application test */
|
||||||
class AppTest {
|
class AppTest {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Issue: Add at least one assertion to this test case.
|
* 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}
|
* <p>Solution: Inserted assertion to check whether the execution of the main method in {@link
|
||||||
* throws an exception.
|
* App} throws an exception.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldExecuteApplicationWithoutException() {
|
void shouldExecuteApplicationWithoutException() {
|
||||||
|
|
||||||
assertDoesNotThrow(() -> App.main(new String[]{}));
|
assertDoesNotThrow(() -> App.main(new String[] {}));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+199
-184
@@ -33,7 +33,6 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.mockito.internal.verification.VerificationModeFactory.times;
|
import static org.mockito.internal.verification.VerificationModeFactory.times;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
@@ -43,49 +42,43 @@ import org.mockito.Captor;
|
|||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
/**
|
/** ThreadAsyncExecutorTest */
|
||||||
* ThreadAsyncExecutorTest
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class ThreadAsyncExecutorTest {
|
class ThreadAsyncExecutorTest {
|
||||||
|
|
||||||
@Captor
|
@Captor private ArgumentCaptor<Exception> exceptionCaptor;
|
||||||
private ArgumentCaptor<Exception> exceptionCaptor;
|
|
||||||
|
|
||||||
@Mock
|
@Mock private Callable<Object> task;
|
||||||
private Callable<Object> task;
|
|
||||||
|
|
||||||
@Mock
|
@Mock private AsyncCallback<Object> callback;
|
||||||
private AsyncCallback<Object> callback;
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
MockitoAnnotations.openMocks(this);
|
MockitoAnnotations.openMocks(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} */
|
||||||
* Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)}
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
void testSuccessfulTaskWithoutCallback() {
|
void testSuccessfulTaskWithoutCallback() {
|
||||||
assertTimeout(ofMillis(3000), () -> {
|
assertTimeout(
|
||||||
// Instantiate a new executor and start a new 'null' task ...
|
ofMillis(3000),
|
||||||
final var executor = new ThreadAsyncExecutor();
|
() -> {
|
||||||
|
// Instantiate a new executor and start a new 'null' task ...
|
||||||
|
final var executor = new ThreadAsyncExecutor();
|
||||||
|
|
||||||
final var result = new Object();
|
final var result = new Object();
|
||||||
when(task.call()).thenReturn(result);
|
when(task.call()).thenReturn(result);
|
||||||
|
|
||||||
final var asyncResult = executor.startProcess(task);
|
final var asyncResult = executor.startProcess(task);
|
||||||
assertNotNull(asyncResult);
|
assertNotNull(asyncResult);
|
||||||
asyncResult.await(); // Prevent timing issues, and wait until the result is available
|
asyncResult.await(); // Prevent timing issues, and wait until the result is available
|
||||||
assertTrue(asyncResult.isCompleted());
|
assertTrue(asyncResult.isCompleted());
|
||||||
|
|
||||||
// Our task should only execute once ...
|
// Our task should only execute once ...
|
||||||
verify(task, times(1)).call();
|
verify(task, times(1)).call();
|
||||||
|
|
||||||
// ... and the result should be exactly the same object
|
// ... and the result should be exactly the same object
|
||||||
assertSame(result, asyncResult.getValue());
|
assertSame(result, asyncResult.getValue());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -94,28 +87,30 @@ class ThreadAsyncExecutorTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void testSuccessfulTaskWithCallback() {
|
void testSuccessfulTaskWithCallback() {
|
||||||
assertTimeout(ofMillis(3000), () -> {
|
assertTimeout(
|
||||||
// Instantiate a new executor and start a new 'null' task ...
|
ofMillis(3000),
|
||||||
final var executor = new ThreadAsyncExecutor();
|
() -> {
|
||||||
|
// Instantiate a new executor and start a new 'null' task ...
|
||||||
|
final var executor = new ThreadAsyncExecutor();
|
||||||
|
|
||||||
final var result = new Object();
|
final var result = new Object();
|
||||||
when(task.call()).thenReturn(result);
|
when(task.call()).thenReturn(result);
|
||||||
|
|
||||||
final var asyncResult = executor.startProcess(task, callback);
|
final var asyncResult = executor.startProcess(task, callback);
|
||||||
assertNotNull(asyncResult);
|
assertNotNull(asyncResult);
|
||||||
asyncResult.await(); // Prevent timing issues, and wait until the result is available
|
asyncResult.await(); // Prevent timing issues, and wait until the result is available
|
||||||
assertTrue(asyncResult.isCompleted());
|
assertTrue(asyncResult.isCompleted());
|
||||||
|
|
||||||
// Our task should only execute once ...
|
// Our task should only execute once ...
|
||||||
verify(task, times(1)).call();
|
verify(task, times(1)).call();
|
||||||
|
|
||||||
// ... same for the callback, we expect our object
|
// ... same for the callback, we expect our object
|
||||||
verify(callback, times(1)).onComplete(eq(result));
|
verify(callback, times(1)).onComplete(eq(result));
|
||||||
verify(callback, times(0)).onError(exceptionCaptor.capture());
|
verify(callback, times(0)).onError(exceptionCaptor.capture());
|
||||||
|
|
||||||
// ... and the result should be exactly the same object
|
// ... and the result should be exactly the same object
|
||||||
assertSame(result, asyncResult.getValue());
|
assertSame(result, asyncResult.getValue());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -124,38 +119,43 @@ class ThreadAsyncExecutorTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void testLongRunningTaskWithoutCallback() {
|
void testLongRunningTaskWithoutCallback() {
|
||||||
assertTimeout(ofMillis(5000), () -> {
|
assertTimeout(
|
||||||
// Instantiate a new executor and start a new 'null' task ...
|
ofMillis(5000),
|
||||||
final var executor = new ThreadAsyncExecutor();
|
() -> {
|
||||||
|
// Instantiate a new executor and start a new 'null' task ...
|
||||||
|
final var executor = new ThreadAsyncExecutor();
|
||||||
|
|
||||||
final var result = new Object();
|
final var result = new Object();
|
||||||
when(task.call()).thenAnswer(i -> {
|
when(task.call())
|
||||||
Thread.sleep(1500);
|
.thenAnswer(
|
||||||
return result;
|
i -> {
|
||||||
});
|
Thread.sleep(1500);
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
final var asyncResult = executor.startProcess(task);
|
final var asyncResult = executor.startProcess(task);
|
||||||
assertNotNull(asyncResult);
|
assertNotNull(asyncResult);
|
||||||
assertFalse(asyncResult.isCompleted());
|
assertFalse(asyncResult.isCompleted());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
asyncResult.getValue();
|
asyncResult.getValue();
|
||||||
fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
|
fail(
|
||||||
} catch (IllegalStateException e) {
|
"Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
|
||||||
assertNotNull(e.getMessage());
|
} catch (IllegalStateException e) {
|
||||||
}
|
assertNotNull(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
// Our task should only execute once, but it can take a while ...
|
// Our task should only execute once, but it can take a while ...
|
||||||
verify(task, timeout(3000).times(1)).call();
|
verify(task, timeout(3000).times(1)).call();
|
||||||
|
|
||||||
// Prevent timing issues, and wait until the result is available
|
// Prevent timing issues, and wait until the result is available
|
||||||
asyncResult.await();
|
asyncResult.await();
|
||||||
assertTrue(asyncResult.isCompleted());
|
assertTrue(asyncResult.isCompleted());
|
||||||
verifyNoMoreInteractions(task);
|
verifyNoMoreInteractions(task);
|
||||||
|
|
||||||
// ... and the result should be exactly the same object
|
// ... and the result should be exactly the same object
|
||||||
assertSame(result, asyncResult.getValue());
|
assertSame(result, asyncResult.getValue());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -164,42 +164,47 @@ class ThreadAsyncExecutorTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void testLongRunningTaskWithCallback() {
|
void testLongRunningTaskWithCallback() {
|
||||||
assertTimeout(ofMillis(5000), () -> {
|
assertTimeout(
|
||||||
// Instantiate a new executor and start a new 'null' task ...
|
ofMillis(5000),
|
||||||
final var executor = new ThreadAsyncExecutor();
|
() -> {
|
||||||
|
// Instantiate a new executor and start a new 'null' task ...
|
||||||
|
final var executor = new ThreadAsyncExecutor();
|
||||||
|
|
||||||
final var result = new Object();
|
final var result = new Object();
|
||||||
when(task.call()).thenAnswer(i -> {
|
when(task.call())
|
||||||
Thread.sleep(1500);
|
.thenAnswer(
|
||||||
return result;
|
i -> {
|
||||||
});
|
Thread.sleep(1500);
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
final var asyncResult = executor.startProcess(task, callback);
|
final var asyncResult = executor.startProcess(task, callback);
|
||||||
assertNotNull(asyncResult);
|
assertNotNull(asyncResult);
|
||||||
assertFalse(asyncResult.isCompleted());
|
assertFalse(asyncResult.isCompleted());
|
||||||
|
|
||||||
verifyNoMoreInteractions(callback);
|
verifyNoMoreInteractions(callback);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
asyncResult.getValue();
|
asyncResult.getValue();
|
||||||
fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
|
fail(
|
||||||
} catch (IllegalStateException e) {
|
"Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
|
||||||
assertNotNull(e.getMessage());
|
} catch (IllegalStateException e) {
|
||||||
}
|
assertNotNull(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
// Our task should only execute once, but it can take a while ...
|
// Our task should only execute once, but it can take a while ...
|
||||||
verify(task, timeout(3000).times(1)).call();
|
verify(task, timeout(3000).times(1)).call();
|
||||||
verify(callback, timeout(3000).times(1)).onComplete(eq(result));
|
verify(callback, timeout(3000).times(1)).onComplete(eq(result));
|
||||||
verify(callback, times(0)).onError(isA(Exception.class));
|
verify(callback, times(0)).onError(isA(Exception.class));
|
||||||
|
|
||||||
// Prevent timing issues, and wait until the result is available
|
// Prevent timing issues, and wait until the result is available
|
||||||
asyncResult.await();
|
asyncResult.await();
|
||||||
assertTrue(asyncResult.isCompleted());
|
assertTrue(asyncResult.isCompleted());
|
||||||
verifyNoMoreInteractions(task, callback);
|
verifyNoMoreInteractions(task, callback);
|
||||||
|
|
||||||
// ... and the result should be exactly the same object
|
// ... and the result should be exactly the same object
|
||||||
assertSame(result, asyncResult.getValue());
|
assertSame(result, asyncResult.getValue());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -209,35 +214,40 @@ class ThreadAsyncExecutorTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void testEndProcess() {
|
void testEndProcess() {
|
||||||
assertTimeout(ofMillis(5000), () -> {
|
assertTimeout(
|
||||||
// Instantiate a new executor and start a new 'null' task ...
|
ofMillis(5000),
|
||||||
final var executor = new ThreadAsyncExecutor();
|
() -> {
|
||||||
|
// Instantiate a new executor and start a new 'null' task ...
|
||||||
|
final var executor = new ThreadAsyncExecutor();
|
||||||
|
|
||||||
final var result = new Object();
|
final var result = new Object();
|
||||||
when(task.call()).thenAnswer(i -> {
|
when(task.call())
|
||||||
Thread.sleep(1500);
|
.thenAnswer(
|
||||||
return result;
|
i -> {
|
||||||
});
|
Thread.sleep(1500);
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
final var asyncResult = executor.startProcess(task);
|
final var asyncResult = executor.startProcess(task);
|
||||||
assertNotNull(asyncResult);
|
assertNotNull(asyncResult);
|
||||||
assertFalse(asyncResult.isCompleted());
|
assertFalse(asyncResult.isCompleted());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
asyncResult.getValue();
|
asyncResult.getValue();
|
||||||
fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
|
fail(
|
||||||
} catch (IllegalStateException e) {
|
"Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
|
||||||
assertNotNull(e.getMessage());
|
} catch (IllegalStateException e) {
|
||||||
}
|
assertNotNull(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
assertSame(result, executor.endProcess(asyncResult));
|
assertSame(result, executor.endProcess(asyncResult));
|
||||||
verify(task, times(1)).call();
|
verify(task, times(1)).call();
|
||||||
assertTrue(asyncResult.isCompleted());
|
assertTrue(asyncResult.isCompleted());
|
||||||
|
|
||||||
// Calling end process a second time while already finished should give the same result
|
// Calling end process a second time while already finished should give the same result
|
||||||
assertSame(result, executor.endProcess(asyncResult));
|
assertSame(result, executor.endProcess(asyncResult));
|
||||||
verifyNoMoreInteractions(task);
|
verifyNoMoreInteractions(task);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -246,25 +256,28 @@ class ThreadAsyncExecutorTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void testNullTask() {
|
void testNullTask() {
|
||||||
assertTimeout(ofMillis(3000), () -> {
|
assertTimeout(
|
||||||
// Instantiate a new executor and start a new 'null' task ...
|
ofMillis(3000),
|
||||||
final var executor = new ThreadAsyncExecutor();
|
() -> {
|
||||||
final var asyncResult = executor.startProcess(null);
|
// Instantiate a new executor and start a new 'null' task ...
|
||||||
|
final var executor = new ThreadAsyncExecutor();
|
||||||
|
final var asyncResult = executor.startProcess(null);
|
||||||
|
|
||||||
assertNotNull(asyncResult, "The AsyncResult should not be 'null', even though the task was 'null'.");
|
assertNotNull(
|
||||||
asyncResult.await(); // Prevent timing issues, and wait until the result is available
|
asyncResult,
|
||||||
assertTrue(asyncResult.isCompleted());
|
"The AsyncResult should not be 'null', even though the task was 'null'.");
|
||||||
|
asyncResult.await(); // Prevent timing issues, and wait until the result is available
|
||||||
try {
|
assertTrue(asyncResult.isCompleted());
|
||||||
asyncResult.getValue();
|
|
||||||
fail("Expected ExecutionException with NPE as cause");
|
|
||||||
} catch (final ExecutionException e) {
|
|
||||||
assertNotNull(e.getMessage());
|
|
||||||
assertNotNull(e.getCause());
|
|
||||||
assertEquals(NullPointerException.class, e.getCause().getClass());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
asyncResult.getValue();
|
||||||
|
fail("Expected ExecutionException with NPE as cause");
|
||||||
|
} catch (final ExecutionException e) {
|
||||||
|
assertNotNull(e.getMessage());
|
||||||
|
assertNotNull(e.getCause());
|
||||||
|
assertEquals(NullPointerException.class, e.getCause().getClass());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -273,32 +286,35 @@ class ThreadAsyncExecutorTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void testNullTaskWithCallback() {
|
void testNullTaskWithCallback() {
|
||||||
assertTimeout(ofMillis(3000), () -> {
|
assertTimeout(
|
||||||
// Instantiate a new executor and start a new 'null' task ...
|
ofMillis(3000),
|
||||||
final var executor = new ThreadAsyncExecutor();
|
() -> {
|
||||||
final var asyncResult = executor.startProcess(null, callback);
|
// Instantiate a new executor and start a new 'null' task ...
|
||||||
|
final var executor = new ThreadAsyncExecutor();
|
||||||
|
final var asyncResult = executor.startProcess(null, callback);
|
||||||
|
|
||||||
assertNotNull(asyncResult, "The AsyncResult should not be 'null', even though the task was 'null'.");
|
assertNotNull(
|
||||||
asyncResult.await(); // Prevent timing issues, and wait until the result is available
|
asyncResult,
|
||||||
assertTrue(asyncResult.isCompleted());
|
"The AsyncResult should not be 'null', even though the task was 'null'.");
|
||||||
verify(callback, times(0)).onComplete(any());
|
asyncResult.await(); // Prevent timing issues, and wait until the result is available
|
||||||
verify(callback, times(1)).onError(exceptionCaptor.capture());
|
assertTrue(asyncResult.isCompleted());
|
||||||
|
verify(callback, times(0)).onComplete(any());
|
||||||
|
verify(callback, times(1)).onError(exceptionCaptor.capture());
|
||||||
|
|
||||||
final var exception = exceptionCaptor.getValue();
|
final var exception = exceptionCaptor.getValue();
|
||||||
assertNotNull(exception);
|
assertNotNull(exception);
|
||||||
|
|
||||||
assertEquals(NullPointerException.class, exception.getClass());
|
assertEquals(NullPointerException.class, exception.getClass());
|
||||||
|
|
||||||
try {
|
|
||||||
asyncResult.getValue();
|
|
||||||
fail("Expected ExecutionException with NPE as cause");
|
|
||||||
} catch (final ExecutionException e) {
|
|
||||||
assertNotNull(e.getMessage());
|
|
||||||
assertNotNull(e.getCause());
|
|
||||||
assertEquals(NullPointerException.class, e.getCause().getClass());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
asyncResult.getValue();
|
||||||
|
fail("Expected ExecutionException with NPE as cause");
|
||||||
|
} catch (final ExecutionException e) {
|
||||||
|
assertNotNull(e.getMessage());
|
||||||
|
assertNotNull(e.getCause());
|
||||||
|
assertEquals(NullPointerException.class, e.getCause().getClass());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -307,28 +323,27 @@ class ThreadAsyncExecutorTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void testNullTaskWithNullCallback() {
|
void testNullTaskWithNullCallback() {
|
||||||
assertTimeout(ofMillis(3000), () -> {
|
assertTimeout(
|
||||||
// Instantiate a new executor and start a new 'null' task ...
|
ofMillis(3000),
|
||||||
final var executor = new ThreadAsyncExecutor();
|
() -> {
|
||||||
final var asyncResult = executor.startProcess(null, null);
|
// Instantiate a new executor and start a new 'null' task ...
|
||||||
|
final var executor = new ThreadAsyncExecutor();
|
||||||
|
final var asyncResult = executor.startProcess(null, null);
|
||||||
|
|
||||||
assertNotNull(
|
assertNotNull(
|
||||||
asyncResult,
|
asyncResult,
|
||||||
"The AsyncResult should not be 'null', even though the task and callback were 'null'."
|
"The AsyncResult should not be 'null', even though the task and callback were 'null'.");
|
||||||
);
|
asyncResult.await(); // Prevent timing issues, and wait until the result is available
|
||||||
asyncResult.await(); // Prevent timing issues, and wait until the result is available
|
assertTrue(asyncResult.isCompleted());
|
||||||
assertTrue(asyncResult.isCompleted());
|
|
||||||
|
|
||||||
try {
|
|
||||||
asyncResult.getValue();
|
|
||||||
fail("Expected ExecutionException with NPE as cause");
|
|
||||||
} catch (final ExecutionException e) {
|
|
||||||
assertNotNull(e.getMessage());
|
|
||||||
assertNotNull(e.getCause());
|
|
||||||
assertEquals(NullPointerException.class, e.getCause().getClass());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
asyncResult.getValue();
|
||||||
|
fail("Expected ExecutionException with NPE as cause");
|
||||||
|
} catch (final ExecutionException e) {
|
||||||
|
assertNotNull(e.getMessage());
|
||||||
|
assertNotNull(e.getCause());
|
||||||
|
assertEquals(NullPointerException.class, e.getCause().getClass());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -34,6 +34,14 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>balking</artifactId>
|
<artifactId>balking</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
|||||||
@@ -26,9 +26,7 @@ package com.iluwatar.balking;
|
|||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/** An interface to simulate delay while executing some work. */
|
||||||
* An interface to simulate delay while executing some work.
|
|
||||||
*/
|
|
||||||
public interface DelayProvider {
|
public interface DelayProvider {
|
||||||
void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task);
|
void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task);
|
||||||
}
|
}
|
||||||
@@ -28,30 +28,26 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
/**
|
/** Washing machine class. */
|
||||||
* Washing machine class.
|
|
||||||
*/
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class WashingMachine {
|
public class WashingMachine {
|
||||||
|
|
||||||
private final DelayProvider delayProvider;
|
private final DelayProvider delayProvider;
|
||||||
|
|
||||||
@Getter
|
@Getter private WashingMachineState washingMachineState;
|
||||||
private WashingMachineState washingMachineState;
|
|
||||||
|
|
||||||
/**
|
/** Creates a new instance of WashingMachine. */
|
||||||
* Creates a new instance of WashingMachine.
|
|
||||||
*/
|
|
||||||
public WashingMachine() {
|
public WashingMachine() {
|
||||||
this((interval, timeUnit, task) -> {
|
this(
|
||||||
try {
|
(interval, timeUnit, task) -> {
|
||||||
Thread.sleep(timeUnit.toMillis(interval));
|
try {
|
||||||
} catch (InterruptedException ie) {
|
Thread.sleep(timeUnit.toMillis(interval));
|
||||||
LOGGER.error("", ie);
|
} catch (InterruptedException ie) {
|
||||||
Thread.currentThread().interrupt();
|
LOGGER.error("", ie);
|
||||||
}
|
Thread.currentThread().interrupt();
|
||||||
task.run();
|
}
|
||||||
});
|
task.run();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -63,9 +59,7 @@ public class WashingMachine {
|
|||||||
this.washingMachineState = WashingMachineState.ENABLED;
|
this.washingMachineState = WashingMachineState.ENABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Method responsible for washing if the object is in appropriate state. */
|
||||||
* Method responsible for washing if the object is in appropriate state.
|
|
||||||
*/
|
|
||||||
public void wash() {
|
public void wash() {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
var machineState = getWashingMachineState();
|
var machineState = getWashingMachineState();
|
||||||
@@ -81,12 +75,9 @@ public class WashingMachine {
|
|||||||
this.delayProvider.executeAfterDelay(50, TimeUnit.MILLISECONDS, this::endOfWashing);
|
this.delayProvider.executeAfterDelay(50, TimeUnit.MILLISECONDS, this::endOfWashing);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Method is responsible for ending the washing by changing machine state. */
|
||||||
* Method is responsible for ending the washing by changing machine state.
|
|
||||||
*/
|
|
||||||
public synchronized void endOfWashing() {
|
public synchronized void endOfWashing() {
|
||||||
washingMachineState = WashingMachineState.ENABLED;
|
washingMachineState = WashingMachineState.ENABLED;
|
||||||
LOGGER.info("{}: Washing completed.", Thread.currentThread().getId());
|
LOGGER.info("{}: Washing completed.", Thread.currentThread().getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -24,27 +24,22 @@
|
|||||||
*/
|
*/
|
||||||
package com.iluwatar.balking;
|
package com.iluwatar.balking;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.function.Executable;
|
import org.junit.jupiter.api.function.Executable;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
/** Application test */
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Application test
|
|
||||||
*/
|
|
||||||
class AppTest {
|
class AppTest {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Issue: Add at least one assertion to this test case.
|
* 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}
|
* <p>Solution: Inserted assertion to check whether the execution of the main method in {@link
|
||||||
* throws an exception.
|
* App} throws an exception.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldExecuteApplicationWithoutException() {
|
void shouldExecuteApplicationWithoutException() {
|
||||||
assertDoesNotThrow((Executable) App::main);
|
assertDoesNotThrow((Executable) App::main);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
@@ -29,9 +29,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
/**
|
/** Tests for {@link WashingMachine} */
|
||||||
* Tests for {@link WashingMachine}
|
|
||||||
*/
|
|
||||||
class WashingMachineTest {
|
class WashingMachineTest {
|
||||||
|
|
||||||
private final FakeDelayProvider fakeDelayProvider = new FakeDelayProvider();
|
private final FakeDelayProvider fakeDelayProvider = new FakeDelayProvider();
|
||||||
@@ -69,4 +67,4 @@ class WashingMachineTest {
|
|||||||
this.task = task;
|
this.task = task;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-6
@@ -38,7 +38,7 @@
|
|||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-api</artifactId>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -53,11 +53,6 @@
|
|||||||
<version>3.27.3</version>
|
<version>3.27.3</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>junit</groupId>
|
|
||||||
<artifactId>junit</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
|||||||
@@ -30,17 +30,15 @@ import java.util.List;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The Bloc class is responsible for managing the current state and notifying registered listeners
|
* The Bloc class is responsible for managing the current state and notifying registered listeners
|
||||||
* whenever the state changes. It implements the ListenerManager interface, allowing listeners
|
* whenever the state changes. It implements the ListenerManager interface, allowing listeners to be
|
||||||
* to be added, removed, and notified of state changes.
|
* added, removed, and notified of state changes.
|
||||||
*/
|
*/
|
||||||
public class Bloc implements ListenerManager<State> {
|
public class Bloc implements ListenerManager<State> {
|
||||||
|
|
||||||
private State currentState;
|
private State currentState;
|
||||||
private final List<StateListener<State>> listeners = new ArrayList<>();
|
private final List<StateListener<State>> listeners = new ArrayList<>();
|
||||||
|
|
||||||
/**
|
/** Constructs a new Bloc instance with an initial state of value 0. */
|
||||||
* Constructs a new Bloc instance with an initial state of value 0.
|
|
||||||
*/
|
|
||||||
public Bloc() {
|
public Bloc() {
|
||||||
this.currentState = new State(0);
|
this.currentState = new State(0);
|
||||||
}
|
}
|
||||||
@@ -88,16 +86,12 @@ public class Bloc implements ListenerManager<State> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Increments the current state value by 1 and notifies listeners of the change. */
|
||||||
* Increments the current state value by 1 and notifies listeners of the change.
|
|
||||||
*/
|
|
||||||
public void increment() {
|
public void increment() {
|
||||||
emitState(new State(currentState.value() + 1));
|
emitState(new State(currentState.value() + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Decrements the current state value by 1 and notifies listeners of the change. */
|
||||||
* Decrements the current state value by 1 and notifies listeners of the change.
|
|
||||||
*/
|
|
||||||
public void decrement() {
|
public void decrement() {
|
||||||
emitState(new State(currentState.value() - 1));
|
emitState(new State(currentState.value() - 1));
|
||||||
}
|
}
|
||||||
|
|||||||
Loaded 100 of 1863 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user