comments, tests and description
This commit is contained in:
		| @@ -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. | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| ## 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/) | ||||
|   | ||||
| @@ -14,6 +14,10 @@ | ||||
|             <artifactId>junit</artifactId> | ||||
|             <scope>test</scope> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>com.google.guava</groupId> | ||||
|             <artifactId>guava</artifactId> | ||||
|         </dependency> | ||||
|     </dependencies> | ||||
|     <artifactId>converter</artifactId> | ||||
|  | ||||
|   | ||||
| @@ -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<UserDto, User> 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<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); | ||||
|  | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -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 <T> DTO representation's type | ||||
|  * @param <U> Domain representation's type | ||||
|  */ | ||||
| @@ -47,37 +50,37 @@ public class Converter<T, U> { | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * @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<U> createFromDtos(final Collection<T> arg) { | ||||
|     return arg.stream().map(this::convertFromDto).collect(Collectors.toList()); | ||||
|   public final List<U> createFromDtos(final Collection<T> 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<T> createFromEntities(final Collection<U> arg) { | ||||
|     return arg.stream().map(this::convertFromEntity).collect(Collectors.toList()); | ||||
|   public final List<T> createFromEntities(final Collection<U> users) { | ||||
|     return users.stream().map(this::convertFromEntity).collect(Collectors.toList()); | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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 + '\'' + '}'; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -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())); | ||||
|   } | ||||
| } | ||||
| @@ -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 + '\'' + '}'; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -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); | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user