DB에서 꺼낸 데이터를 저장하는 Entitiy를 가지고 만드는 일종의 Wrapper Class
데이터 전송 객체는 프로세스간 데이터를 전달하는 객체이다. 프로세스 간 통신이 일반적으로 원격 인터페이스로 재정렬하면서 이루어지게 되는데 여기에서 각 호출의 비용이 많다는 점을 동기로 하여 이용하게 된다.
DTO는 순수하게 데이터를 저장하고, 데이터에 대한 getter, setter 만을 가져야한다고 한다.
DTO는 어떠한 비즈니스 로직을 가져서는 안되며, 저장, 검색, 직렬화, 역직렬화 로직만을 가져야 한다고 한다.
🤔 기존에는 그냥 setter를 통해 값을 변경해주었는데 지양하라는 이유는?
setter를 사용한다면 엔티티의 변경이 필요할 때 외부에서 직접 상태를 변경하게 되며, entity의 내부 구현이 외부 요인에 의해 쉽게 변경될 수 있게 되어 OCP에 반함
set으로만 되어있어 어떤 의도와 목적을 가지고 값을 변경하였는지 알 수 없다!
빌더 패턴을 사용해 객체를 얻는 방법은 다음과 같다.
필수 매개변수가 담긴 생성자(혹은 정적 팩토리 메서드)를 호출해 빌더 객체를 얻는다.
빌더 객체가 제공하는 메서드-일종의 setter 메서드-들로 원하는 선택 매개변수를 설정한다.
매개변수가 없는 build 메서드를 호출해 필요한 객체를 얻는다.
대게 빌더는 생성할 클래스 안에 정적 멤버 클래스로 만들어둔다.
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
public static class Builder {
private final int servingSize; // 필수
private final int servings; // 필수
private int calories = 0; // 선택
private int fat = 0; // 선택
private int sodium = 0; // 선택
private int carbohydrate = 0; // 선택
public Builder(int servingSize, int servings) { // 1.
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val) { // 2.
calories = val;
return this;
}
public Builder fat(int val) { // 2.
fat = val;
return this;
}
public Builder sodium(int val) { // 2.
sodium = val;
return this;
}
public Builder carbohydrate(int val) { // 2.
carbohydrate = val;
return this;
}
public NutritionFacts build() { // 3.
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
@Override
public String toString() { // 편의상 작성한 것으로 빌더 패턴과는 무관하다.
return "servingSize : " + servingSize
+ "\nservings : " + servings
+ "\ncalories : " + calories
+ "\nfat : " + fat
+ "\nsodium : " + sodium
+ "\ncarbohydrate : " + carbohydrate;
}
public static void main(String[] args) {
NutritionFacts n = new Builder(240, 8)
.calories(10)
.fat(10)
.build();
System.out.println(n.toString());
}
}
setter와는 다르게 불변성 보장!
setter와는 다르게 반환 타입이 Builder이기 때문에 메서드 체이닝 가능!
객체의 필드가 많을 때 유용 (with Lombok)
코드가 이해하기 어렵다..
필드가 중복이 되믈로 반드시 좋다고 말할 수 없다..
코드가 너무 복잡해서 가독성이 떨어지는데 이를 롬복 라이브러리로 해결 가능하다!
import lombok.Builder;
@Builder
public class NutritionFacts {
private int servingSize;
private int servings;
private int calories;
private int fat;
public static void main(String[] args) {
NutritionFacts n = NutritionFacts.builder()
.calories(2)
.build();
NutritionFacts n2 = new NutritionFacts(20, 5, 1, 1);
}
}
- setter로 값을 수정할 때 의도를 파악하기 힘들다.
- 그것을 해결하기 위해 builder 패턴이 나왔다.
- 코드가 복잡해지는 불편함을 Lombok이 해결해준다.
- builder 패턴으로 DTO를 관리하자!
https://yeoooo.github.io/java/BuilderPattern/
setter를 지양하고 Builder 패턴을 사용하자
[Spring] @Setter vs @Builder
Entity에 setter를 사용하지 않는 진짜 이유
Java setter vs builder
[Java] Builder pattern 특징과 장단점 with. setter