In this post, we will discuss most commonly used Converter Design Pattern in Java/J2EE projects. Thanks to Java 8 features not only provides a way of generic bidirectional conversion between corresponding types but also a common way of converting a collection of objects of the same type, reducing the boilerplate code to the absolute minimum. we have written source code for this pattern using Java 8 features.
Intent
The purpose of the Converter Design 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 Design Pattern introduces bidirectional collection mapping, reducing a boilerplate code to a minimum.
Source code
The Converter Design 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.
Class diagram
Let's write source code according to the above Class diagram.
In this example, we are converting CustomerDto to Customer entity and vice versa also converting a collection of objects between types.
Step 1: Let's create a Generic converter.
public abstract class Converter < T, C > {
private final Function < T,
C > fromDto;
private final Function < C,
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, C > fromDto, final Function < C, T > fromEntity) {
this.fromDto = fromDto;
this.fromEntity = fromEntity;
}
/**
* @param customerDto
* DTO entity
* @return The domain representation - the result of the converting function
* application on dto entity.
*/
public final C convertFromDto(final T customerDto) {
return fromDto.apply(customerDto);
}
/**
* @param customer
* domain entity
* @return The DTO representation - the result of the converting function
* application on domain entity.
*/
public final T convertFromEntity(final C customer) {
return fromEntity.apply(customer);
}
/**
* @param dtoCustomers
* collection of DTO entities
* @return List of domain representation of provided entities retrieved by
* mapping each of them with the conversion function
*/
public final List < C > createFromDtos(final Collection < T > dtoCustomers) {
return dtoCustomers.stream().map(this::convertFromDto).collect(Collectors.toList());
}
/**
* @param customers
* collection of domain entities
* @return List of domain representation of provided entities retrieved by
* mapping each of them with the conversion function
*/
public final List < T > createFromEntities(final Collection < C > customers) {
return customers.stream().map(this::convertFromEntity).collect(Collectors.toList());
}
}
Step 2: Let's create an implementation of the simple Customer converter.
public class CustomerConverter extends Converter<CustomerDto, Customer> {
public CustomerConverter() {
super(customerDto -> new Customer(customerDto.getCustomerId(), customerDto.getCustomerName(),
customerDto.getCustomerLastName(), customerDto.isStatus()),
customer -> new CustomerDto(customer.getCustomerId(), customer.getCustomerName(),
customer.getCustomerLastName(), customer.isStatus()));
}
}
Step 3: Create CustomerDto class.
public class CustomerDto {
private String customerId;
private String customerName;
private String customerLastName;
private boolean status;
public CustomerDto(String customerId, String customerName, String customerLastName, boolean status) {
super();
this.customerId = customerId;
this.customerName = customerName;
this.customerLastName = customerLastName;
this.status = status;
}
public String getCustomerId() {
return customerId;
}
public void setCustomerId(String customerId) {
this.customerId = customerId;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public String getCustomerLastName() {
return customerLastName;
}
public void setCustomerLastName(String customerLastName) {
this.customerLastName = customerLastName;
}
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
}
Step 4: Create domain Customer entity class.
public class Customer {
private String customerId;
private String customerName;
private String customerLastName;
private boolean status;
public Customer(String customerId, String customerName, String customerLastName, boolean status) {
super();
this.customerId = customerId;
this.customerName = customerName;
this.customerLastName = customerLastName;
this.status = status;
}
public String getCustomerId() {
return customerId;
}
public void setCustomerId(String customerId) {
this.customerId = customerId;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public String getCustomerLastName() {
return customerLastName;
}
public void setCustomerLastName(String customerLastName) {
this.customerLastName = customerLastName;
}
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
}
Step 5: Now, let's test this pattern via creating Client class.
public class Client {
/**
* Program entry point
*
* @param args command line args
*/
public static void main(String[] args) {
Converter < CustomerDto, Customer > CustomerConverter = new CustomerConverter();
CustomerDto dtoCustomer = new CustomerDto("100", "Ramesh", "Fadatare", true);
Customer Customer = CustomerConverter.convertFromDto(dtoCustomer);
System.out.println("Entity converted from DTO:" + Customer);
List < Customer > customers = new ArrayList < > ();
customers.add(new Customer("100", "Ramesh1", "Fadatare", true));
customers.add(new Customer("200", "Ramesh2", "Fadatare", true));
customers.add(new Customer("300", "Ramesh3", "Fadatare", true));
customers.forEach(System.out::println);
customers.forEach((customer) - > System.out.println(customer.getCustomerId()));
System.out.println("DTO entities converted from domain:");
List < CustomerDto > dtoEntities = CustomerConverter.createFromEntities(customers);
dtoEntities.forEach(System.out::println);
dtoEntities.forEach((customer) - > System.out.println(customer.getCustomerId()));
}
}
Output:
Entity converted from DTO:com.ramesh.j2ee.converter.Customer@87aac27
com.ramesh.j2ee.converter.Customer@1b28cdfa
com.ramesh.j2ee.converter.Customer@eed1f14
com.ramesh.j2ee.converter.Customer@7229724f
100
200
300
DTO entities converted from domain:
com.ramesh.j2ee.converter.CustomerDto@4dd8dc3
com.ramesh.j2ee.converter.CustomerDto@6d03e736
com.ramesh.j2ee.converter.CustomerDto@568db2f2
100
200
300
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.
Related Patterns
- Data Transfer Object Design Pattern in Java
- Service Locator Design Pattern in Java
- Business Delegate Design Pattern in Java
- Converter Design Pattern in Java
- Transfer Object Assembler Pattern in Java
- Value List Handler Pattern in Java
Thanks for sharing this great source of information. Click here here for some great stuff
ReplyDeleteGrateful for the help, but I'm having trouble converting objects. For example: TeacherDto is inside my ClassroomDto object. And to convert, I end up having problems because I can't invoke conversion methods inside the DTO object.
ReplyDelete