Skip to content

Commit 8f777cd

Browse files
committed
refactor: converter pattern docs and refactoring
1 parent 20e804b commit 8f777cd

File tree

6 files changed

+87
-93
lines changed

6 files changed

+87
-93
lines changed

converter/README.md

+73-47
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,62 @@
11
---
22
title: Converter
3-
category: Creational
3+
category: Structural
44
language: en
55
tag:
6-
- Decoupling
6+
- Compatibility
7+
- Data transformation
8+
- Object mapping
79
---
810

11+
## Also known as
12+
13+
* Mapper
14+
* Translator
15+
916
## Intent
1017

11-
The purpose of the Converter pattern is to provide a generic, common way of bidirectional
12-
conversion between corresponding types, allowing a clean implementation in which the types do not
13-
need to be aware of each other. Moreover, the Converter pattern introduces bidirectional collection
14-
mapping, reducing a boilerplate code to minimum.
18+
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.
1519

1620
## Explanation
1721

1822
Real world example
1923

20-
> In real world applications it is often the case that database layer consists of entities that need
21-
> to be mapped into DTOs for use on the business logic layer. Similar mapping is done for
22-
> potentially huge amount of classes and we need a generic way to achieve this.
24+
> In real world applications it is often the case that database layer consists of entities that need to be mapped into DTOs for use on the business logic layer. Similar mapping is done for potentially huge amount of classes, and we need a generic way to achieve this.
2325
2426
In plain words
2527

2628
> Converter pattern makes it easy to map instances of one class into instances of another class.
2729
2830
**Programmatic Example**
2931

30-
We need a generic solution for the mapping problem. To achieve this, let's introduce a generic
31-
converter.
32+
We need a generic solution for the mapping problem. To achieve this, let's introduce a generic converter.
3233

3334
```java
3435
public class Converter<T, U> {
3536

36-
private final Function<T, U> fromDto;
37-
private final Function<U, T> fromEntity;
37+
private final Function<T, U> fromDto;
38+
private final Function<U, T> fromEntity;
3839

39-
public Converter(final Function<T, U> fromDto, final Function<U, T> fromEntity) {
40-
this.fromDto = fromDto;
41-
this.fromEntity = fromEntity;
42-
}
40+
public Converter(final Function<T, U> fromDto, final Function<U, T> fromEntity) {
41+
this.fromDto = fromDto;
42+
this.fromEntity = fromEntity;
43+
}
4344

44-
public final U convertFromDto(final T dto) {
45-
return fromDto.apply(dto);
46-
}
45+
public final U convertFromDto(final T dto) {
46+
return fromDto.apply(dto);
47+
}
4748

48-
public final T convertFromEntity(final U entity) {
49-
return fromEntity.apply(entity);
50-
}
49+
public final T convertFromEntity(final U entity) {
50+
return fromEntity.apply(entity);
51+
}
5152

52-
public final List<U> createFromDtos(final Collection<T> dtos) {
53-
return dtos.stream().map(this::convertFromDto).collect(Collectors.toList());
54-
}
53+
public final List<U> createFromDtos(final Collection<T> dtos) {
54+
return dtos.stream().map(this::convertFromDto).collect(Collectors.toList());
55+
}
5556

56-
public final List<T> createFromEntities(final Collection<U> entities) {
57-
return entities.stream().map(this::convertFromEntity).collect(Collectors.toList());
58-
}
57+
public final List<T> createFromEntities(final Collection<U> entities) {
58+
return entities.stream().map(this::convertFromEntity).collect(Collectors.toList());
59+
}
5960
}
6061
```
6162

@@ -64,27 +65,26 @@ The specialized converters inherit from this base class as follows.
6465
```java
6566
public class UserConverter extends Converter<UserDto, User> {
6667

67-
public UserConverter() {
68-
super(UserConverter::convertToEntity, UserConverter::convertToDto);
69-
}
70-
71-
private static UserDto convertToDto(User user) {
72-
return new UserDto(user.getFirstName(), user.getLastName(), user.isActive(), user.getUserId());
73-
}
68+
public UserConverter() {
69+
super(UserConverter::convertToEntity, UserConverter::convertToDto);
70+
}
7471

75-
private static User convertToEntity(UserDto dto) {
76-
return new User(dto.getFirstName(), dto.getLastName(), dto.isActive(), dto.getEmail());
77-
}
72+
private static UserDto convertToDto(User user) {
73+
return new UserDto(user.firstName(), user.lastName(), user.active(), user.userId());
74+
}
7875

76+
private static User convertToEntity(UserDto dto) {
77+
return new User(dto.firstName(), dto.lastName(), dto.active(), dto.email());
78+
}
7979
}
8080
```
8181

8282
Now mapping between `User` and `UserDto` becomes trivial.
8383

8484
```java
85-
var userConverter = new UserConverter();
86-
var dtoUser = new UserDto("John", "Doe", true, "whatever[at]wherever.com");
87-
var user = userConverter.convertFromDto(dtoUser);
85+
var userConverter=new UserConverter();
86+
var dtoUser=new UserDto("John","Doe",true,"whatever[at]wherever.com");
87+
var user=userConverter.convertFromDto(dtoUser);
8888
```
8989

9090
## Class diagram
@@ -95,11 +95,37 @@ var user = userConverter.convertFromDto(dtoUser);
9595

9696
Use the Converter Pattern in the following situations:
9797

98-
* When you have types that logically correspond with each other and you need to convert entities
99-
between them.
100-
* When you want to provide different ways of types conversions depending on the context.
101-
* Whenever you introduce a DTO (Data transfer object), you will probably need to convert it into the
102-
domain equivalence.
98+
* When there are types that logically correspond with each other, and there is a need to convert between them.
99+
* In applications that interact with external systems or services that require data in a specific format.
100+
* For legacy systems integration where data models differ significantly from newer systems.
101+
* When aiming to encapsulate conversion logic to promote single responsibility and cleaner code.
102+
103+
## Known Uses
104+
105+
* Data Transfer Objects (DTOs) conversions in multi-layered applications.
106+
* Adapting third-party data structures or API responses to internal models.
107+
* ORM (Object-Relational Mapping) frameworks for mapping between database records and domain objects.
108+
* Microservices architecture for data exchange between different services.
109+
110+
## Consequences
111+
112+
Benefits:
113+
114+
* Separation of Concerns: Encapsulates conversion logic in a single component, keeping the rest of the application unaware of the conversion details.
115+
* Reusability: Converter components can be reused across the application or even in different applications.
116+
* Flexibility: Makes it easy to add new conversions without impacting existing code, adhering to the [Open/Closed Principle](https://java-design-patterns.com/principles/#open-closed-principle).
117+
* Interoperability: Facilitates communication between different systems or application layers by translating data formats.
118+
119+
Trade-offs:
120+
121+
* Overhead: Introducing converters can add complexity and potential performance overhead, especially in systems with numerous data formats.
122+
* Duplication: There's a risk of duplicating model definitions if not carefully managed, leading to increased maintenance.
123+
124+
## Related Patterns
125+
126+
* [Adapter](https://java-design-patterns.com/patterns/adapter/): Similar in intent to adapting interfaces, but Converter focuses on data models.
127+
* [Facade](https://java-design-patterns.com/patterns/facade/): Provides a simplified interface to a complex system, which might involve data conversion.
128+
* [Strategy](https://java-design-patterns.com/patterns/strategy/): Converters can use different strategies for conversion, especially when multiple formats are involved.
103129

104130
## Credits
105131

converter/src/main/java/com/iluwatar/converter/User.java

+2-16
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,7 @@
2424
*/
2525
package com.iluwatar.converter;
2626

27-
import lombok.EqualsAndHashCode;
28-
import lombok.Getter;
29-
import lombok.RequiredArgsConstructor;
30-
import lombok.ToString;
31-
3227
/**
33-
* User class.
28+
* User record.
3429
*/
35-
@ToString
36-
@EqualsAndHashCode
37-
@Getter
38-
@RequiredArgsConstructor
39-
public class User {
40-
private final String firstName;
41-
private final String lastName;
42-
private final boolean active;
43-
private final String userId;
44-
}
30+
public record User(String firstName, String lastName, boolean active, String userId) {}

converter/src/main/java/com/iluwatar/converter/UserConverter.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,10 @@ public UserConverter() {
3434
}
3535

3636
private static UserDto convertToDto(User user) {
37-
return new UserDto(user.getFirstName(), user.getLastName(), user.isActive(), user.getUserId());
37+
return new UserDto(user.firstName(), user.lastName(), user.active(), user.userId());
3838
}
3939

4040
private static User convertToEntity(UserDto dto) {
41-
return new User(dto.getFirstName(), dto.getLastName(), dto.isActive(), dto.getEmail());
41+
return new User(dto.firstName(), dto.lastName(), dto.active(), dto.email());
4242
}
43-
4443
}

converter/src/main/java/com/iluwatar/converter/UserDto.java

+2-18
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,7 @@
2424
*/
2525
package com.iluwatar.converter;
2626

27-
import lombok.EqualsAndHashCode;
28-
import lombok.Getter;
29-
import lombok.RequiredArgsConstructor;
30-
import lombok.ToString;
31-
3227
/**
33-
* User DTO class.
28+
* UserDto record.
3429
*/
35-
@RequiredArgsConstructor
36-
@Getter
37-
@EqualsAndHashCode
38-
@ToString
39-
public class UserDto {
40-
41-
private final String firstName;
42-
private final String lastName;
43-
private final boolean active;
44-
private final String email;
45-
46-
}
30+
public record UserDto(String firstName, String lastName, boolean active, String email) {}

converter/src/test/java/com/iluwatar/converter/AppTest.java

-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ class AppTest {
3535

3636
/**
3737
* Issue: Add at least one assertion to this test case.
38-
*
3938
* Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])}
4039
* throws an exception.
4140
*/

converter/src/test/java/com/iluwatar/converter/ConverterTest.java

+8-8
Original file line numberDiff line numberDiff line change
@@ -65,20 +65,20 @@ void testConversionsStartingFromDto() {
6565
void testCustomConverter() {
6666
var converter = new Converter<UserDto, User>(
6767
userDto -> new User(
68-
userDto.getFirstName(),
69-
userDto.getLastName(),
70-
userDto.isActive(),
68+
userDto.firstName(),
69+
userDto.lastName(),
70+
userDto.active(),
7171
String.valueOf(new Random().nextInt())
7272
),
7373
user -> new UserDto(
74-
user.getFirstName(),
75-
user.getLastName(),
76-
user.isActive(),
77-
user.getFirstName().toLowerCase() + user.getLastName().toLowerCase() + "@whatever.com")
74+
user.firstName(),
75+
user.lastName(),
76+
user.active(),
77+
user.firstName().toLowerCase() + user.lastName().toLowerCase() + "@whatever.com")
7878
);
7979
var u1 = new User("John", "Doe", false, "12324");
8080
var userDto = converter.convertFromEntity(u1);
81-
assertEquals("[email protected]", userDto.getEmail());
81+
assertEquals("[email protected]", userDto.email());
8282
}
8383

8484
/**

0 commit comments

Comments
 (0)