반응형
빌더 패턴(Builder Pattern)은 객체를 생성할 때 사용하는 패턴을 의미한다.
//빌더 패턴 예시
Mebmer customer = Member.build()
.name("홍길동")
.age(30)
.build();
위는 자바 코드다. 위와 같은 예시 코드가 빌더 패턴이라고 볼 수 있겠다.
Builder 패턴을 사용하는 이유
- 객체 생성을 깔끔하게 하기 위함
- 점층적 생성자 패턴(telescoping constructor pattern)
- 자바빈 패턴 = 점층적 생성자 패턴의 대안
- 빌더 패턴 = 자바빈 패턴의 대안
- 객체 생성을 유연하게 하기 위함
- 빌더 인터페이스를 작성하여 다양한 빌더를 사용하는 방법
객체 생성을 '깔끔하게' 하기 위한 빌더 패턴 사용
점층적 생성자 패턴
점층적 생성자 패턴을 만드는 방법은 다음과 같다.
- 필수 인자를 받는 필수 생성자를 하나 만든다.
- 1개의 선택적 인자를 받는 생성자를 추가한다.
- 2개의 선택적 인자를 받는 생성자를 추가한다.
- 최종적으로 모든 선택적 인자를 다 받는 생성자를 추가한다.
// 점층적 생성자 패턴 코드 예시 : 회원가입 관련 코드
public class Member {
private final String name; //필수 인자
private final String location; //선택적 인자
private final String hobby; //선택적 인자
/** 필수 생성자 **/
public Member(String name) {
this(name, "출신지역 비공개", "취미 비공개");
}
/** 1개의 선택적 인자를 받는 생성자 **/
public Member(String name, String location) {
this(name, location, "취미 비공개");
}
/** 모든 선택적 인자를 다 받는 생성자 **/
public Member(String name, String location, String hobby) {
this.name = name;
this.location = location;
this.hobby = hobby;
}
}
-
장점 : new Member("홍길동", "출신지역 비공개", "취미 비공개") 같은 호출이 빈번하게 일어난다면, new Member("홍길동")로 대체할 수 있다.
-
단점
-
생성자가 많으므로, 인자가 추가되는 일이 발생하면 코드 수정 다소 복잡.
-
가독성 저하 => 특히 인자수가 많을 때 호출 코드만 봐서는 의미를 알기 어렵다.
//가독성 저하의 예시 NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 3, 35, 27); NutritionFacts pepsiCola = new NutritionFacts(220, 10, 110, 4, 30); NutritionFacts mountainDew = new NutritionFacts(230, 10);
-
자바빈 패턴(JavaBeans Pattern)
//자바빈 패턴 예시 : setter 메소드 사용
NutritionFacts cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setCarbohydrate(27);
- 장점
- 인자 의미 파악 용이
- 점층적 생성자 패턴처럼 여러 생성자 만들 필요 X
- 단점
- 객체 일관성(consistency)이 깨진다.
- 1회의 호출로 객체 생성이 끝나지 않음 (생성한 객체에 값을 계속 넣어주고 있음)
- setter 메소드가 있으므로 변경 불가능(immutable) 클래스를 만들 수가 없다.
- Thread 안전성을 확보하려면 점층적 생성자 패턴보다 많은 일을 해야 한다.
- 객체 일관성(consistency)이 깨진다.
빌더 패턴
//builder pattern 예시
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 carbohydrate = 0;
private int sodium = 0;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val) {
calories = val;
return this; // 해당 방식으로 인해 . 으로 체인을 이어갈 수 있다.
}
public Builder fat(int val) {
fat = val;
return this;
}
public Builder carbohydrate(int val) {
carvohyddrate = val;
return this;
}
public Builder sodium(int val) {
sodium = val;
return this;
}
public NutritionFacts build() {
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;
}
}
위와 같은 방식으로 다음과 같이 객체를 생성할 수 있다.
// 객체 생성 예시
NutritionFacts.Builder builder = new NutritionFacts.Builder(240, 8);
builder.calories(100);
builder.sodium(35);
builder.carbohydrate(27);
NutritionFacts cocaCola = builder.build();
//또 다른 사용 예시
/** 각 줄마다 builder를 타이핑하지 않아도 되어 편리 **/
NutritionFacts cocaCola = new NutritionFacts
.Builder(240, 8) //필수값 입력
.calories(100)
.sodium(35)
carbohydrate(27)
.build(); //build() 가 객체를 생성해 돌려준다.
- 장점
- 각 인자가 어떤 의미인지 알기 쉬움
- setter 메소드가 없으므로 변경 불가능 객체를 만들 수 있다.
- 한 번에 객체를 생성하므로 객체 일관성이 깨지지 않는다.
- build() 함수가 잘못된 값이 입력되었는지 검증하게 할 수도 있다.
해당 빌더 패턴을 사용하고 싶다면 lombok 이라는 라이브러리의 @Builder 어노테이션으로 쉽게 사용할 수 있다. 다음은 그 예시
//lombok 사용 예시
@Builder
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 class Example {
/** 일반 사용 예시 **/
NutritionFacts.NutritionFactsBuilder builder = NutritionFacts.builder();
builder.calories(230);
builder.fat(10);
NutritionFacts facts = builder.build();
/** 체인 사용 예시 **/
NutritionFacts facts = NutritionFacts.builder()
.calories(230)
.fat(10)
.build();
}
객체 생성을 '유연하게' 하기 위한 빌더 패턴 사용
빌더 패턴을 사용하면 하나의 빌더 객체로 여러 객체를 만드는 것도 가능하다.
// 하나의 빌더 객체로 여러 객체 만드는 예시
public interface Builder<T> {
public T build();
}
위와 같은 인터페이스를 만들고, 빌더 클래스가 implements 하게 하면 된다.
Reference : https://johngrib.github.io/wiki/builder-pattern/
반응형
'개발 지식 > Java' 카테고리의 다른 글
[Java] Jetty 란? (0) | 2020.04.06 |
---|---|
Thread Pool이란? (0) | 2020.04.04 |
[JAVA] Logback 사용법 (0) | 2020.02.19 |
[Java] Generic 사용법 (0) | 2020.02.10 |
[JAVA] JDBC란 (1) | 2020.02.05 |