From 8632bafcd7dfc28c141dd24ad258da9abe951c10 Mon Sep 17 00:00:00 2001 From: Kamil Pietruszka Date: Sat, 11 Mar 2017 12:24:48 +0100 Subject: [PATCH] comments, tests and description --- .../iluwatar/api/gateway/ImageClientImpl.java | 2 +- .../iluwatar/api/gateway/PriceClientImpl.java | 2 +- converter/README.md | 16 +++-- converter/pom.xml | 4 ++ .../main/java/com/iluwatar/converter/App.java | 29 ++++++++- .../com/iluwatar/converter/Converter.java | 27 +++++---- .../java/com/iluwatar/converter/User.java | 45 +++++++++----- .../com/iluwatar/converter/UserConverter.java | 40 +++++++++++++ .../java/com/iluwatar/converter/UserDto.java | 46 ++++++++++----- .../com/iluwatar/converter/ConverterTest.java | 59 +++++++++++++++++++ 10 files changed, 219 insertions(+), 51 deletions(-) create mode 100644 converter/src/main/java/com/iluwatar/converter/UserConverter.java create mode 100644 converter/src/test/java/com/iluwatar/converter/ConverterTest.java 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 index cbca98e8f..190ae8bfc 100644 --- a/converter/README.md +++ b/converter/README.md @@ -10,16 +10,20 @@ tags: --- ## Intent -TODO +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 "TODO") +![alt text](./etc/converter.png "Converter Pattern") ## Applicability -TODO +Use the Converter Pattern in the following situations: -* TODO 1 -* TODO 2 +* 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://todo.com) +* [Converter](http://www.xsolve.pl/blog/converter-pattern-in-java-8/) diff --git a/converter/pom.xml b/converter/pom.xml index 53eca720b..026f30d40 100644 --- a/converter/pom.xml +++ b/converter/pom.xml @@ -14,6 +14,10 @@ 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 index 91fbb98f8..1d6076fd9 100644 --- a/converter/src/main/java/com/iluwatar/converter/App.java +++ b/converter/src/main/java/com/iluwatar/converter/App.java @@ -22,6 +22,18 @@ */ 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 @@ -30,11 +42,22 @@ public class App { */ public static void main(String[] args) { Converter userConverter = new Converter<>( - userDto -> new User(userDto.getFirstName(), userDto.getLastName(), userDto.isActive()), - user -> new UserDto(user.getFirstName(), user.getLastName(), user.isActive())); - UserDto dtoUser = new UserDto("John", "Doe", true); + 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); UserDto dtoUserCopy = userConverter.convertFromEntity(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 index 7f7a702cd..eeabc4102 100644 --- a/converter/src/main/java/com/iluwatar/converter/Converter.java +++ b/converter/src/main/java/com/iluwatar/converter/Converter.java @@ -29,6 +29,9 @@ 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 */ @@ -47,37 +50,37 @@ public class Converter { } /** - * @param arg DTO entity + * @param userDto DTO entity * @return The domain representation - the result of the converting function application on dto entity. */ - public U convertFromDto(final T arg) { - return fromDto.apply(arg); + public final U convertFromDto(final T userDto) { + return fromDto.apply(userDto); } /** - * @param arg domain entity + * @param user domain entity * @return The DTO representation - the result of the converting function application on domain entity. */ - public T convertFromEntity(final U arg) { - return fromEntity.apply(arg); + public final T convertFromEntity(final U user) { + return fromEntity.apply(user); } /** - * @param arg collection of DTO entities + * @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 List createFromDtos(final Collection arg) { - return arg.stream().map(this::convertFromDto).collect(Collectors.toList()); + public final List createFromDtos(final Collection dtoUsers) { + return dtoUsers.stream().map(this::convertFromDto).collect(Collectors.toList()); } /** - * @param arg collection of domain entities + * @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 List createFromEntities(final Collection arg) { - return arg.stream().map(this::convertFromEntity).collect(Collectors.toList()); + 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 index 8a4e9186c..f40c01e79 100644 --- a/converter/src/main/java/com/iluwatar/converter/User.java +++ b/converter/src/main/java/com/iluwatar/converter/User.java @@ -23,44 +23,61 @@ 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 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) { + 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 void setFirstName(String firstName) { - this.firstName = firstName; - } - public String getLastName() { return lastName; } - public void setLastName(String lastName) { - this.lastName = lastName; - } - public boolean isActive() { return isActive; } - public void setActive(boolean active) { - isActive = active; + 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 index bdadf6e39..8f55bbe0e 100644 --- a/converter/src/main/java/com/iluwatar/converter/UserDto.java +++ b/converter/src/main/java/com/iluwatar/converter/UserDto.java @@ -24,44 +24,62 @@ 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 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) { + 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 void setFirstName(String firstName) { - this.firstName = firstName; - } - public String getLastName() { return lastName; } - public void setLastName(String lastName) { - this.lastName = lastName; - } - public boolean isActive() { return isActive; } - public void setActive(boolean active) { - isActive = active; + 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/ConverterTest.java b/converter/src/test/java/com/iluwatar/converter/ConverterTest.java new file mode 100644 index 000000000..45cebc4e6 --- /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); + } +}