
이런 컨트롤러 코드를 역할 분리를 통해 리팩토링하려 한다.
1. API의 진입 지점으로써의 역할은 Controller에 남겨둔다.
2. 예외 처리 및 추가적인 로직 구현은 Service 함수로 보낸다.
3. SQL을 사용하여 DB와의 통신을 담당하는 부분은 Repository로 보낸다.
@RestController
public class UserController {
private final UserService userService;
public UserController(JdbcTemplate jdbcTemplate){
this.userService = new UserService(jdbcTemplate);
}
@PostMapping("/user") //POST /user
public void saveUser(@RequestBody UserCreateRequest request){
userService.saveUser(request.getName(), request.getAge());
}
@GetMapping("/user")
public List<UserResponse> getUsers(){
return userService.getUser();
}
@PutMapping("/user")
public void updateUser(@RequestBody UserUpdateRequest request){
userService.updateUser(request);
}
@DeleteMapping("/user")
public void deleteUser(@RequestParam String name){
userService.deleteUser(name);
}
}
public class UserService {
private final UserRepository userRepository;
public UserService(JdbcTemplate jdbcTemplate) {
this.userRepository = new UserRepository(jdbcTemplate);
}
public void saveUser(String name, int age){
userRepository.saveUserToDB(name, age);
}
public List<UserResponse> getUser(){
return userRepository.getUserFromDB();
}
public void updateUser(UserUpdateRequest request){
if(userRepository.checkUserExist(request.getId())){
throw new IllegalArgumentException();
}
userRepository.updateUserToDB(request);
}
public void deleteUser(String name){
String readSql = "DELETE * FROM user WHERE name = ?";
if(userRepository.isUserNotExist(name)){
throw new IllegalArgumentException();
}
userRepository.deleteUserToDB(name);
}
}
public class UserRepository {
private final JdbcTemplate jdbcTemplate;
public UserRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void saveUserToDB(String name, int age){
String sql = "INSERT INTO user (name, age) VALUES(?, ?)";
jdbcTemplate.update(sql,name, age);
}
public List<UserResponse> getUserFromDB(){
String sql = "select * from user";
return jdbcTemplate.query(sql, (rs, rowNum) -> {
long id = rs.getLong("id");
String name = rs.getString("name");
int age = rs.getInt("age");
return new UserResponse(id, name, age);
});
}
public boolean checkUserExist(long id){
String readSql = "SELECT * FROM user WHERE id = ?";
return jdbcTemplate.query(readSql, (rs, rowNum)-> 0, id).isEmpty();
}
public void updateUserToDB(UserUpdateRequest request){
String sql = "UPDATE user SET name = ? WHERE id = ?";
jdbcTemplate.update(sql, request.getName(), request.getId());
}
public boolean isUserNotExist(String name){
String readSql = "DELETE * FROM user WHERE name = ?";
return jdbcTemplate.query(readSql, (rs, rowNum)-> 0, name).isEmpty();
}
public void deleteUserToDB(String name){
String sql = "DELETE FROM user WHERE name = ?";
jdbcTemplate.update(sql, name);
}
}
관심사의 분리를 통해 코드 리팩토링을 진행했고, jdbcTemplate를 계속해서 전달하지 않고 생성자에 jdbcTemplate을 가진 채로 혹은 객체를 생성할 때 전달해주면서 함수에서의 인자를 줄였다.
클린 코드에 대한 정리
최대한 책임을 분리시키고 메소드와 변수명을 이해하기 쉽도록 작성하였다. 코드 역시 이해하기 쉽게 하고 길이도 짧게 구현하였다.
public class DiceGame {
public static void main(String[] args) throws Exception{
Player player = new Player();
Dice dice = new Dice(6, player.getNumberOfDiceRoll());
dice.roll();
dice.printResult();
System.exit(0);
}
}
import java.util.Scanner;
public class Player {
Scanner input = new Scanner(System.in);
public int getNumberOfDiceRoll(){
System.out.print("숫자를 입력하시오 : ");
return input.nextInt();
}
}
import java.util.Arrays;
public class Dice {
public final int diceMaxNum;
public int[] numCount;
public int executeNum;
public Dice(int diceMaxNum, int executeNum) {
this.diceMaxNum = diceMaxNum;
this.numCount = new int[diceMaxNum];
Arrays.fill(numCount, 0);
this.executeNum = executeNum;
}
public void roll(){
for(int i = 0; i < executeNum; i++){
this.numCount[(int)(Math.random() * 6)] += 1;
}
}
public void printResult(){
for(int i=0; i < numCount.length; i++){
System.out.println((i+1) + "은 "+ numCount[i] +"번 나왔습니다.");
}
}
}
Dice.printResult도 엄연히 다른 역할이기 때문에 다른 클래스로 분리할까 고민했지만, 클래스 자체가 너무 많아지고, Dice 클래스 내부에서 다른 메소드로도 책임 분리가 가능하다고 생각하여 이렇게 구현하였다.