Merge pull request #546 from Crossy147/converter

Converter
This commit is contained in:
Ilkka Seppälä 2017-04-01 14:17:37 +03:00 committed by GitHub
commit 952c207a62
14 changed files with 559 additions and 3 deletions

View File

@ -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

View File

@ -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

29
converter/README.md Normal file
View File

@ -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/)

BIN
converter/etc/Converter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.14" icons="true" automaticImage="PNG" always-add-relationships="false"
generalizations="true" realizations="true" associations="true" dependencies="false" nesting-relationships="true"
router="FAN">
<class id="1" language="java" name="com.iluwatar.converter.Converter" project="converter"
file="/converter/src/main/java/com/iluwatar/converter/Converter.java" binary="false" corner="BOTTOM_RIGHT">
<position height="189" width="226" x="41" y="37"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="2" language="java" name="com.iluwatar.converter.UserConverter" project="converter"
file="/converter/src/main/java/com/iluwatar/converter/UserConverter.java" binary="false" corner="BOTTOM_RIGHT">
<position height="81" width="107" x="41" y="356"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="3" language="java" name="com.iluwatar.converter.User" project="converter"
file="/converter/src/main/java/com/iluwatar/converter/User.java" binary="false" corner="BOTTOM_RIGHT">
<position height="279" width="188" x="307" y="37"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="4" language="java" name="com.iluwatar.converter.UserDto" project="converter"
file="/converter/src/main/java/com/iluwatar/converter/UserDto.java" binary="false" corner="BOTTOM_RIGHT">
<position height="279" width="204" x="535" y="37"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<generalization id="5">
<end type="SOURCE" refId="2"/>
<end type="TARGET" refId="1"/>
</generalization>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/>
</class-diagram>

25
converter/pom.xml Normal file
View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.15.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
</dependencies>
<artifactId>converter</artifactId>
</project>

View File

@ -0,0 +1,63 @@
/**
* The MIT License
* Copyright (c) 2014-2016 Ilkka Seppälä
* <p>
* 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:
* <p>
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* <p>
* 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<UserDto, User> 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<User> 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<UserDto> dtoEntities = userConverter.createFromEntities(users);
dtoEntities.forEach(System.out::println);
}
}

View File

@ -0,0 +1,86 @@
/**
* The MIT License
* Copyright (c) 2014-2016 Ilkka Seppälä
* <p>
* 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:
* <p>
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* <p>
* 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 <T> DTO representation's type
* @param <U> Domain representation's type
*/
public class Converter<T, U> {
private final Function<T, U> fromDto;
private final Function<U, T> 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<T, U> fromDto, final Function<U, T> 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<U> createFromDtos(final Collection<T> 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<T> createFromEntities(final Collection<U> users) {
return users.stream().map(this::convertFromEntity).collect(Collectors.toList());
}
}

View File

@ -0,0 +1,83 @@
/**
* The MIT License
* Copyright (c) 2014-2016 Ilkka Seppälä
* <p>
* 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:
* <p>
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* <p>
* 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 + '\'' + '}';
}
}

View File

@ -0,0 +1,40 @@
/**
* The MIT License
* Copyright (c) 2014-2016 Ilkka Seppälä
* <p>
* 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:
* <p>
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* <p>
* 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<UserDto, User> {
/**
* 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()));
}
}

View File

@ -0,0 +1,85 @@
/**
* The MIT License
* Copyright (c) 2014-2016 Ilkka Seppälä
* <p>
* 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:
* <p>
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* <p>
* 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 + '\'' + '}';
}
}

View File

@ -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);
}
}

View File

@ -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<UserDto, User> 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<User> users = Lists.newArrayList(new User("Camile", "Tough", false, "124sad"),
new User("Marti", "Luther", true, "42309fd"), new User("Kate", "Smith", true, "if0243"));
List<User> fromDtos = userConverter.createFromDtos(userConverter.createFromEntities(users));
assertEquals(fromDtos, users);
}
}

View File

@ -135,9 +135,9 @@
<module>event-asynchronous</module>
<module>queue-load-leveling</module>
<module>object-mother</module>
<module>converter</module>
<module>guarded-suspension</module>
<module>balking</module>
</modules>
<dependencyManagement>