이번 플젝을 하다가 @Builder 어노테이션을 알게 되어 한번 정리해보고자 한다.
1. 빌더패턴 이란?
생성과 관련된 디자인 패턴으로, 동일한 프로세스를 거쳐 다양한 구성의 인스턴스를 만드는 방법으로
필수 값에 대해서는 생성자를 통해, 선택적인 값들에 대해서는 메소드를 통해 step-by-step으로 값을 입력받은 후에
build() 메소드를 통해 최종적으로 하나의 인스턴스를 return 하는 방식이다.
-1) 정의
- GoF 디자인 패턴 중 생성 패턴에 해당
- 빌더 패턴은 복잡한 객체를 생성하는 클래스와 표현하는 클래스를 분리하여, 동일한 절차에서도 서로 다른 표현을 생성하는 방법 제공
- 생성해야하는 객체가 Optional 한 속성을 많이 가질 때 더 좋음.
쉽게 말해 생성자에서 인자가 많을때 고려해볼 수 있는 것이 빌더 패턴이다.
2. 빌더패턴 생성 이유
클래스를 설계할 때 필수로 받아야 하는 변수가 있고 선택적으로 받아야 할 변수가 있다.
이때 생성자로만 구성하게 된다면 필수로 받아야 하는 생성자를 하나 생성하고 추가적인 변수에 따라 생성자를 더 선언해주어야 한다.
이러한 패턴을 Effectivce Java 2/E에서 "점증적 생성자 패턴"이라고 한다.
public class Person{
private String name; //필수 정보
private int age;
private int phonNumber;
public Person(String name){
this.name = name;
}
public Person(String name, int age){
this.name = name;
this.age = age;
}
public Person(String name, int age, int phonNumber){
this.name = name;
this.age = age;
this.phonNumber = phonNumber;
}
}
점증적 생성자 패턴의 단점은 아래와 같다.
- 인자들이 많아질수록 생성자가 많아진다.
- 매개변수의 정보를 설명할 수 없으므로 어떤 객체에 어떤 인자가 들어갔는지 알기 어렵다.
- 주입 시 개발자가 매개변수의 어떤 위치에 어떤 타입과 값을 넣어줘야 하는지 알고 있어야 한다.
이러한 단점을 보완하기 위해 "자바 빈즈 패턴"이 나오게 되었다.
자바 빈즈 패턴은 set함수를 생성해 줌으로써 추가적으로 들어오는 정보를 set함수를 사용해 집어넣어주는 것이다.
해당 패턴은 점증적 생성자 패턴의 문제점이었던 가독성 문제가 해결되긴 하지만
가장 큰 문제로 객체의 일관성이 깨진다는 점이다.
다시 말해 set 함수로 객체 값이 쉽게 바뀔 수 있다는 점이다.
public class Person{
private String name; //필수정보
private int age;
private int phonNumber;
/**생성자*/
public Person(){
}
public void setName(String name){ this.name = name; }
public void setAge(int age){ this.age = age; }
public void setPhonNumber(int phonNumber){ this.phonNumber = phonNumber; }
}
위의 두 패턴의 단점을 모두 보안해서 나온 것이 빌더패턴이다.
정보들은 자바 빈즈 패턴처럼 받되, 데이터 일관성을 위해 정보들을 다 받은 후에 객체를 생성한다.
빌더 패턴의 장점은 아래와 같다.
- 불필요한 생성자 제거
- 데이터 일관성 보장
- 데이터 순서와 무관
- 각 인자의 의미를 명시적으로 알기 쉬움
- null 체크 가능
public class Example<T> {
private T foo;
private final String bar;
/**생성자*/
private Example(T foo, String bar) {
this.foo = foo;
this.bar = bar;
}
public static <T> ExampleBuilder<T> builder() {
return new ExampleBuilder<T>();
}
public static class ExampleBuilder<T> {
private T foo;
private String bar;
private ExampleBuilder() {}
public ExampleBuilder foo(T foo) {
this.foo = foo;
return this;
}
public ExampleBuilder bar(String bar) {
this.bar = bar;
return this;
}
public Example build() {
return new Example(foo, bar);
}
}
}
이렇게 빌더를 생성해주면 객체를 생성할 때는 아래와 같이 생성해주면 된다.
Example<Person> example= new Example<person>
.Builder()
.bar("Test")
.build()
3. Lombok @Builder
매번 위와 같이 static 함수를 구현하는 건 귀찮은 일이라.. 롬복에서 해당 어노테이션으로 위와 같은 기능을 지원해 준다.
@Builder
public class Example<T> {
private T foo;
private final String bar;
}
실무에서는 Stream.Builder API, StringBuilder, UriComponentsBuilder 등에 활용된다고 한다.
끝!
출처 및 참고:
https://esoongan.tistory.com/82
'👩💻 개발 > ⚙️ BACKEND' 카테고리의 다른 글
[SpringBoot/JPA] 무한 참조 및 type definition error: (simple type) 문제 (0) | 2022.08.18 |
---|---|
[Spring/Jpa] @Default 설정 (0) | 2022.08.12 |
[Spring/Java] modelMapper (4) | 2022.08.11 |
[SpringBoot] Jpa Repository와 Querydsl 사용방법 (0) | 2022.08.06 |
[SpringBoot] Querydsl 설정 (1) | 2022.08.06 |