diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java index 10f8625c5..ebabfe839 100644 --- a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java +++ b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java @@ -35,7 +35,7 @@ import java.io.IOException; * An adapter to communicate with the Image microservice */ @Component -public class ImageClientImpl implements ImageClient{ +public class ImageClientImpl implements ImageClient { /** * Makes a simple HTTP Get request to the Image microservice * @return The path to the image diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java index aa2686845..87f44761c 100644 --- a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java +++ b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java @@ -35,7 +35,7 @@ import java.io.IOException; * An adapter to communicate with the Price microservice */ @Component -public class PriceClientImpl implements PriceClient{ +public class PriceClientImpl implements PriceClient { /** * Makes a simple HTTP Get request to the Price microservice * @return The price of the product diff --git a/converter/README.md b/converter/README.md new file mode 100644 index 000000000..190ae8bfc --- /dev/null +++ b/converter/README.md @@ -0,0 +1,29 @@ +--- +layout: pattern +title: Converter +folder: converter +permalink: /patterns/converter/ +categories: +tags: + - Java + - Difficulty-Beginner +--- + +## Intent +The purpose of the Converter Pattern is to provide a generic, common way of bidirectional +conversion between corresponding types, allowing a clean implementation in which the types do not +need to be aware of each other. Moreover, the Converter Pattern introduces bidirectional collection +mapping, reducing a boilerplate code to minimum. + +![alt text](./etc/converter.png "Converter Pattern") + +## Applicability +Use the Converter Pattern in the following situations: + +* When you have types that logically correspond which other and you need to convert entities between them +* When you want to provide different ways of types conversions depending on a context +* Whenever you introduce a DTO (Data transfer object), you will probably need to convert it into the domain equivalence + +## Credits + +* [Converter](http://www.xsolve.pl/blog/converter-pattern-in-java-8/) diff --git a/converter/etc/Converter.png b/converter/etc/Converter.png new file mode 100644 index 000000000..01435ef5a Binary files /dev/null and b/converter/etc/Converter.png differ diff --git a/converter/etc/Converter.ucls b/converter/etc/Converter.ucls new file mode 100644 index 000000000..368657430 --- /dev/null +++ b/converter/etc/Converter.ucls @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/converter/pom.xml b/converter/pom.xml new file mode 100644 index 000000000..026f30d40 --- /dev/null +++ b/converter/pom.xml @@ -0,0 +1,25 @@ + + + + java-design-patterns + com.iluwatar + 1.15.0-SNAPSHOT + + 4.0.0 + + + junit + junit + test + + + com.google.guava + guava + + + converter + + + diff --git a/converter/src/main/java/com/iluwatar/converter/App.java b/converter/src/main/java/com/iluwatar/converter/App.java new file mode 100644 index 000000000..fbae0309d --- /dev/null +++ b/converter/src/main/java/com/iluwatar/converter/App.java @@ -0,0 +1,63 @@ +/** + * The MIT License + * Copyright (c) 2014-2016 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.converter; + + +import com.google.common.collect.Lists; + +import java.util.ArrayList; +import java.util.List; + +/** + * The Converter pattern is a behavioral design pattern which allows a common way of bidirectional + * conversion between corresponding types (e.g. DTO and domain representations of the logically + * isomorphic types). Moreover, the pattern introduces a common way of converting a collection of + * objects between types. + */ +public class App { + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + Converter userConverter = new Converter<>( + userDto -> new User(userDto.getFirstName(), userDto.getLastName(), userDto.isActive(), + userDto.getEmail()), + user -> new UserDto(user.getFirstName(), user.getLastName(), user.isActive(), user.getUserId())); + + UserDto dtoUser = new UserDto("John", "Doe", true, "whatever[at]wherever.com"); + User user = userConverter.convertFromDto(dtoUser); + System.out.println("Entity converted from DTO:" + user); + + ArrayList users = Lists.newArrayList(new User("Camile", "Tough", false, "124sad"), + new User("Marti", "Luther", true, "42309fd"), new User("Kate", "Smith", true, "if0243")); + System.out.println("Domain entities:"); + users.forEach(System.out::println); + + System.out.println("DTO entities converted from domain:"); + List dtoEntities = userConverter.createFromEntities(users); + dtoEntities.forEach(System.out::println); + + } +} diff --git a/converter/src/main/java/com/iluwatar/converter/Converter.java b/converter/src/main/java/com/iluwatar/converter/Converter.java new file mode 100644 index 000000000..eeabc4102 --- /dev/null +++ b/converter/src/main/java/com/iluwatar/converter/Converter.java @@ -0,0 +1,86 @@ +/** + * The MIT License + * Copyright (c) 2014-2016 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.converter; + +import java.util.Collection; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * Generic converter, thanks to Java8 features not only provides a way of generic bidirectional + * conversion between coresponding types, but also a common way of converting a collection of objects + * of the same type, reducing boilerplate code to the absolute minimum. + * @param DTO representation's type + * @param Domain representation's type + */ +public class Converter { + + private final Function fromDto; + private final Function fromEntity; + + /** + * @param fromDto Function that converts given dto entity into the domain entity. + * @param fromEntity Function that converts given domain entity into the dto entity. + */ + public Converter(final Function fromDto, final Function fromEntity) { + this.fromDto = fromDto; + this.fromEntity = fromEntity; + } + + /** + * @param userDto DTO entity + * @return The domain representation - the result of the converting function application on dto entity. + */ + public final U convertFromDto(final T userDto) { + return fromDto.apply(userDto); + } + + /** + * @param user domain entity + * @return The DTO representation - the result of the converting function application on domain entity. + */ + public final T convertFromEntity(final U user) { + return fromEntity.apply(user); + } + + /** + * @param dtoUsers collection of DTO entities + * @return List of domain representation of provided entities retrieved by + * mapping each of them with the convertion function + */ + public final List createFromDtos(final Collection dtoUsers) { + return dtoUsers.stream().map(this::convertFromDto).collect(Collectors.toList()); + } + + /** + * @param users collection of domain entities + * @return List of domain representation of provided entities retrieved by + * mapping each of them with the convertion function + */ + public final List createFromEntities(final Collection users) { + return users.stream().map(this::convertFromEntity).collect(Collectors.toList()); + } + +} diff --git a/converter/src/main/java/com/iluwatar/converter/User.java b/converter/src/main/java/com/iluwatar/converter/User.java new file mode 100644 index 000000000..f40c01e79 --- /dev/null +++ b/converter/src/main/java/com/iluwatar/converter/User.java @@ -0,0 +1,83 @@ +/** + * The MIT License + * Copyright (c) 2014-2016 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.converter; + +import java.util.Objects; + +public class User { + private String firstName; + private String lastName; + private boolean isActive; + private String userId; + + /** + * @param firstName user's first name + * @param lastName user's last name + * @param isActive flag indicating whether the user is active + * @param userId user's identificator + */ + public User(String firstName, String lastName, boolean isActive, String userId) { + this.firstName = firstName; + this.lastName = lastName; + this.isActive = isActive; + this.userId = userId; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } + + public boolean isActive() { + return isActive; + } + + public String getUserId() { + return userId; + } + + @Override public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + User user = (User) o; + return isActive == user.isActive && Objects.equals(firstName, user.firstName) && Objects + .equals(lastName, user.lastName) && Objects.equals(userId, user.userId); + } + + @Override public int hashCode() { + return Objects.hash(firstName, lastName, isActive, userId); + } + + @Override public String toString() { + return "User{" + "firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + + ", isActive=" + isActive + ", userId='" + userId + '\'' + '}'; + } +} diff --git a/converter/src/main/java/com/iluwatar/converter/UserConverter.java b/converter/src/main/java/com/iluwatar/converter/UserConverter.java new file mode 100644 index 000000000..9ef1d03c2 --- /dev/null +++ b/converter/src/main/java/com/iluwatar/converter/UserConverter.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014-2016 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.converter; + +/** + * Example implementation of the simple User converter. + */ +public class UserConverter extends Converter { + + /** + * Constructor. + */ + public UserConverter() { + super(userDto -> new User(userDto.getFirstName(), userDto.getLastName(), userDto.isActive(), + userDto.getEmail()), + user -> new UserDto(user.getFirstName(), user.getLastName(), user.isActive(), + user.getUserId())); + } +} diff --git a/converter/src/main/java/com/iluwatar/converter/UserDto.java b/converter/src/main/java/com/iluwatar/converter/UserDto.java new file mode 100644 index 000000000..8f55bbe0e --- /dev/null +++ b/converter/src/main/java/com/iluwatar/converter/UserDto.java @@ -0,0 +1,85 @@ +/** + * The MIT License + * Copyright (c) 2014-2016 Ilkka Seppälä + *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + *

+ * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.converter; + + +import java.util.Objects; + +public class UserDto { + + private String firstName; + private String lastName; + private boolean isActive; + private String email; + + /** + * @param firstName user's first name + * @param lastName user's last name + * @param isActive flag indicating whether the user is active + * @param email user's email address + */ + public UserDto(String firstName, String lastName, boolean isActive, String email) { + this.firstName = firstName; + this.lastName = lastName; + this.isActive = isActive; + this.email = email; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } + + public boolean isActive() { + return isActive; + } + + public String getEmail() { + return email; + } + + @Override public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + UserDto userDto = (UserDto) o; + return isActive == userDto.isActive && Objects.equals(firstName, userDto.firstName) && Objects + .equals(lastName, userDto.lastName) && Objects.equals(email, userDto.email); + } + + @Override public int hashCode() { + return Objects.hash(firstName, lastName, isActive, email); + } + + @Override public String toString() { + return "UserDto{" + "firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + + ", isActive=" + isActive + ", email='" + email + '\'' + '}'; + } +} diff --git a/converter/src/test/java/com/iluwatar/converter/AppTest.java b/converter/src/test/java/com/iluwatar/converter/AppTest.java new file mode 100644 index 000000000..5198827d2 --- /dev/null +++ b/converter/src/test/java/com/iluwatar/converter/AppTest.java @@ -0,0 +1,35 @@ +/** + * The MIT License + * Copyright (c) 2014-2016 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.converter; + +import org.junit.Test; + +public class AppTest { + + @Test + public void testMain() { + String[] args = {}; + App.main(args); + } + +} diff --git a/converter/src/test/java/com/iluwatar/converter/ConverterTest.java b/converter/src/test/java/com/iluwatar/converter/ConverterTest.java new file mode 100644 index 000000000..eccb81cf9 --- /dev/null +++ b/converter/src/test/java/com/iluwatar/converter/ConverterTest.java @@ -0,0 +1,59 @@ +package com.iluwatar.converter; + +import com.google.common.collect.Lists; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import static junit.framework.TestCase.assertEquals; + +public class ConverterTest { + + private UserConverter userConverter = new UserConverter(); + + /** + * Tests whether a converter created of opposite functions holds equality as a bijection. + */ + @Test public void testConversionsStartingFromDomain() { + User u1 = new User("Tom", "Hanks", true, "tom@hanks.com"); + User u2 = userConverter.convertFromDto(userConverter.convertFromEntity(u1)); + assertEquals(u1, u2); + } + + /** + * Tests whether a converter created of opposite functions holds equality as a bijection. + */ + @Test public void testConversionsStartingFromDto() { + UserDto u1 = new UserDto("Tom", "Hanks", true, "tom@hanks.com"); + UserDto u2 = userConverter.convertFromEntity(userConverter.convertFromDto(u1)); + assertEquals(u1, u2); + } + + /** + * Tests the custom users converter. Thanks to Java8 lambdas, converter can be easily and + * cleanly instantiated allowing various different conversion strategies to be implemented. + */ + @Test public void testCustomConverter() { + Converter converter = new Converter<>( + userDto -> new User(userDto.getFirstName(), userDto.getLastName(), userDto.isActive(), + String.valueOf(new Random().nextInt())), + user -> new UserDto(user.getFirstName(), user.getLastName(), user.isActive(), + user.getFirstName().toLowerCase() + user.getLastName().toLowerCase() + "@whatever.com")); + User u1 = new User("John", "Doe", false, "12324"); + UserDto userDto = converter.convertFromEntity(u1); + assertEquals(userDto.getEmail(), "johndoe@whatever.com"); + } + + /** + * Test whether converting a collection of Users to DTO Users and then converting them back to domain + * users returns an equal collection. + */ + @Test public void testCollectionConversion() { + ArrayList users = Lists.newArrayList(new User("Camile", "Tough", false, "124sad"), + new User("Marti", "Luther", true, "42309fd"), new User("Kate", "Smith", true, "if0243")); + List fromDtos = userConverter.createFromDtos(userConverter.createFromEntities(users)); + assertEquals(fromDtos, users); + } +} diff --git a/pom.xml b/pom.xml index 83b4e752e..4b22adeb6 100644 --- a/pom.xml +++ b/pom.xml @@ -135,9 +135,9 @@ event-asynchronous queue-load-leveling object-mother + converter guarded-suspension balking -