How to Write Better Java Code: Refactoring, DTO Conversion, Lombok, and Design Practices
This article discusses practical Java programming techniques, including proper DTO conversion, use of BeanUtils, Lombok annotations, builder patterns, static constructors, validation with JSR‑303, and design considerations such as refactoring, strategy versus state patterns, to help developers write cleaner, more maintainable backend code.
The author, a software architect, shares a comprehensive guide on improving Java code quality by focusing on clean design, proper DTO handling, and modern tooling. The article begins with a critique of common pitfalls in Java projects and emphasizes the importance of writing code that is both readable and semantically clear.
For DTO conversion, the author demonstrates how to replace repetitive setter calls with BeanUtils.copyProperties and encapsulate conversion logic in a dedicated interface:
public interface DTOConvert<S, T> {
T convert(S s);
}An implementation for converting a UserInputDTO to a User entity is shown:
public class UserInputDTOConvert implements DTOConvert<UserInputDTO, User> {
@Override
public User convert(UserInputDTO userInputDTO) {
User user = new User();
BeanUtils.copyProperties(userInputDTO, user);
return user;
}
}The article then refactors the API to return DTOs instead of entities, improving encapsulation and security:
@PostMapping
public UserOutputDTO addUser(UserInputDTO userInputDTO) {
User user = new UserInputDTOConvert().convert(userInputDTO);
User saved = userService.addUser(user);
return new UserOutDTOConvert().convertToUser(saved);
}Validation is addressed using JSR‑303 annotations. The author adds @NotNull constraints to DTO fields and validates them in controller methods with @Valid and BindingResult:
public class UserDTO {
@NotNull private String username;
@NotNull private int age;
}
@PostMapping
public UserDTO addUser(@Valid UserDTO userDTO, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
// handle validation errors
}
// conversion and service call
}Lombok is introduced to reduce boilerplate. An example shows how @Getter, @Setter, and @Accessors(chain = true) create fluent APIs, while @RequiredArgsConstructor(staticName = "of") provides a static factory method for required fields:
@Accessors(chain = true)
@RequiredArgsConstructor(staticName = "of")
public class Student {
@NonNull private String name;
private int age;
}Builder pattern usage is simplified with Lombok’s @Builder annotation:
@Builder
public class Student {
private String name;
private int age;
}For proxying Spring’s RestTemplate, the author replaces verbose delegation code with Lombok’s @Delegate:
@AllArgsConstructor
public abstract class FilterRestTemplate implements RestOperations {
@Delegate
protected volatile RestTemplate restTemplate;
}The article also discusses design philosophy, contrasting business‑driven and technology‑driven development, and advises developers to continuously refactor, study mature frameworks, and adopt clean‑code principles. It concludes with a list of recommended skills such as UML, clean code practices, and basic Linux commands.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
