@Builder
생성자처럼 객체를 생성하지만 생성자보다 좀 더 편리하다.
Builder 패턴 vs 생성자
//생성자
Person p = new Person("Kim", "Female", 25);
//빌더 패턴
Person p = Person.builder()
.name("Kim")
.gender("Female")
.age(25)
.build();
✅ 생성자보다 파라미터를 파악하기 쉽다 (가독성이 뛰어나다).
✅ 생성자는 정해진 파라미터 순서대로 값을 넣어야하지만, 빌더 패턴은 순서와 상관없이 필드 이름으로 값이 설정된다.
Builder 사용 방법
직접 Builder를 구현하는 방법도 있으나, Lombok의 @Builder 어노테이션을 사용하면 편리하다.
직접 Builder 구현하는 방법
import java.util.Set;
public class BuilderExample {
private String name;
private int age;
BuilderExample(String name, int age) {
this.name = name;
this.age = age;
}
public static BuilderExampleBuilder builder() {
return new BuilderExampleBuilder();
}
//Builder 클래스 (내부 클래스)
public static class BuilderExampleBuilder {
private String name;
private int age;
BuilderExampleBuilder() {
}
public BuilderExampleBuilder name(String name) {
this.name = name;
return this;
}
public BuilderExampleBuilder age(int age) {
this.age = age;
return this;
}
public BuilderExample build() {
return new BuilderExample(name, age, occupations);
}
@java.lang.Override
public String toString() {
return "BuilderExample.BuilderExampleBuilder("name = " + this.name + ", age = " + this.age + ");
}
}
}
코드 양이 어마어마하다.
Lombok의 @Builder 어노테이션 사용
import lombok.Builder;
import lombok.Singular;
import java.util.Set;
@Builder
public class BuilderExample {
@Builder.Default private long created = System.currentTimeMillis();
private String name;
private int age;
@Singular private Set<String> occupations;
}
@Builder만 붙이면 된다. 편리 👍
@Builder로 Dummy 데이터 만들기
테스트시 많은 양의 더미 데이터(가짜 데이터)가 필요할 수 있다.
이때 생성자로 만드는 것보다 @Builder를 사용하면 좀 더 편리하겠지만, 필드마다 데이터 값을 넣어 작성해주어야한다.
이때 객체를 매개변수로 받아 편리하게 Builder를 사용할 수 있도록 리팩토링 할 수 있다.
메서드 추출 리팩토링
@Getter
@Component
public class Store {
//객체를 받아 생성
private Item newItem(Cart cart, Integer id){
return Item.builder()
.id(id)
.order(orderList.get(0))
.option(cart.getOption())
.quantity(cart.getQuantity())
.price(cart.getOption().getPrice() * cart.getQuantity())
.build();
}
//객체 리스트를 받아 생성
private List<Item> itemDummyList(List<Cart> cartList){
AtomicInteger counter = new AtomicInteger(1);
return cartList.stream().map(
cart -> newItem(cart, counter.getAndIncrement())
).collect(Collectors.toList());
}
//객체와 속성을 파라미터로 받아 생성
private Option newOption(Product product, Integer id, String optionName, int price) {
return Option.builder()
.product(product)
.id(id)
.optionName(optionName)
.price(price)
.build();
}
private Cart newCart(Option option, Integer id, Integer quantity){
return Cart.builder()
.id(id)
.user(newUser(1, "ssar"))
.option(option)
.quantity(quantity)
.price(option.getPrice() * quantity)
.build();
}
}
✅ 생성자와 달리 객체만 전달하면 된다.
✅ @Builder로 하나하나 작성하지 않아 편리하다.
@Builder : 클래스 레벨 생성 vs 생성자 레벨 생성
클래스 레벨 Builder : 클래스 위에 @Buidler를 붙이며, 클래스에서 가능한 모든 필더에 대해 빌더 메서드 생성
생성자 레벨 Builder : 생성자 파라미터 필드에 대해서만 빌더 메서드 생성 - 생성자에 존재하지 않은 필드는 빌더 메서드 생성X
=> 생성자 레벨 Builder 추천
생성자 레벨 Builder를 추천하는 이유
클래스 레벨 Builder에서는 @AllArgsConstructor(모든 멤버필드에 대해 매개변수에 대해 매개변수를 받는 기본 생성자)와 같이 효과를 발생시킨다.
=>빌더로 생성되어야하지 않을 매개변수(ex)timestamp)들도 Builder에 노출된다.
=> 생성자의 접근 레벨이 default가 되므로 동일 패키지 내에서 생성자가 호출될 수 있다.
@Getter 와 @Setter
@Getter @Setter
public class Person{
private String name;
private int age;
}
@Getter는 필드의 접근자 메서드를 생성한다.
@Setter는 필드의 설정자 메서드를 생성한다.
Person p = new Person("얌얌",2); //객체 생성
int age = p.getAge(); //접근자 getter
p.setName("김동글"); //설정자 setter
getter와 setter를 사용할 수 있다.
Setter의 단점을 해결한 Builder
OCP 준수
Setter는 setXXX과 같이 수정이 간단하므로 수정에 대해 개방적이다. - OCP 위배
그러나 Builder는 setXXX이 아닌 다른 함수명을 사용하므로 수정에 좀 더 까다롭다 - OCP 준수
생성과 수정 구분 - 가독성 👍
Setter는 생성과 수정 모두에서 사용되므로 생성과 수정이 명확히 코드로 구분되지 않는다.
Builder는 생성에만 사용되고, 수정시 다른 수정 메서드를 사용하기때문에 생성과 수정이 분리되어 가독성이 좋다.
참고
https://projectlombok.org/features/Builder
https://mjoo1106.tistory.com/entry/Spring-Setter-vs-Builder
'Spring > Spring 개발 상식' 카테고리의 다른 글
즉시 로딩과 지연 로딩(+프록시), Fetch Join, Join (0) | 2023.07.13 |
---|---|
JDBC, MyBatis, JPA (0) | 2023.07.12 |
DTO 생성 방법 (0) | 2023.07.10 |
DAO vs DTO vs VO (2) | 2023.07.10 |
Spring Security Test (0) | 2023.07.08 |