In this article, we will discuss the most commonly used Converter Design Pattern in Java / J2EE projects. Because the Java 8 function not only provides a general two-way conversion between corresponding types, but also provides a common method to convert a collection of objects of the same type, so as to reduce the template code to an absolute minimum. We wrote the source code of this pattern using java 8 functions.
objective
The purpose of converter design pattern is to provide a general way for bidirectional conversion between corresponding types, allowing concise implementation of types without knowing each other. In addition, the converter design pattern introduces bidirectional collection mapping to minimize the template code.
source code
The converter design pattern is a behavioral design pattern that allows bidirectional conversion between corresponding types, such as DTO and domain representation of logical isomorphic types. In addition, the pattern introduces a general method of converting a collection of objects between types.
Class diagram
Let's write the source code according to the class diagram above.
In this example, we will convert customerd to customer entity and vice versa. We will also convert the 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 a simple client 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 the 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 the 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 by creating a 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 converter mode when:
- When you have types that logically correspond to other types, you need to convert entities between them
- If you want to provide different types of conversion methods according to the context
- Whenever you introduce DTO (data transfer object), you may need to convert it to domain equivalent.