※ 일본의 블로그 글을 번역한 포스트입니다. 오역 및 의역, 직역이 있을 수 있으며 틀린 내용은 직역해주시면 감사하겠습니다.
Builder 패턴이란?
컨스트럭터에 대해 수 많은 파라미터를 세팅할 필요가 있을 때에 대신해서 사용하도록 추전되는 구현 방법이다. 그러나 이것이 추천되는 것은 컨스트럭터에 설정되어 있는 파라미터 중 필수인 요소가 적을 때에 한정되는 것 같다.
아무튼 용도에 따라 몇 가지 패턴이 있으므로, 여기에서 정리하고자한다.
생성하는 오브젝트의 조건
- 클래스명 : People
- 필드 : String name(필수), Integer age(필수), String hobby(옵션)
- 필수 요소는 null 금지
- People 클래스는 String을 반환값으로 하는 hello메소드가 존재
- 이번에는 Builder 패턴의 비교를 위해 패턴상 필요하지 않은 getter등의 메소드는 생략
Native Builder
Builder 패턴이 아닌, 그저 컨스트럭터. Builder 패턴을 사용하고 싶어지는 것은 이러한 번거로운 일을 하지 않기 위해서 이지만, 비교를 위해 기재해두도록하겠다.
class People {
private String name;
private Integer age;
private String hobby;
People(String name, Integer age, String hobby) {
if (name == null || age == null) {
throw new NullPointerException();
}
this.name = name;
this.age = age;
this.hobby = hobby;
}
// getter,setter...
String hello() {
//...
}
}
// new People("Tom", 12, "BaseBall").hello();
- 기재방법은 단순하다.
- 불필요한 옵션 요소가 늘어나면, 컨스트럭터에 대량의 null등을 넘겨야할 필요가 있다.
- 모든 값을 인수로 전달하므로, 전달하고 있는 값이 무엇을 나타내는지 확인하기 어렵거나 틀리기 쉽다.
GoF Builder
GoF (오브젝트 지향에 있어서 재이용을 위한 디자인 패턴)으로 소개되는 Builder 패턴이다.
class People {
private String name;
private Integer age;
private String hobby;
// getter,setter...
String hello() {
//...
}
}
class Director {
private Builder builder;
Director(Builder builder) {
this.builder = builder;
}
void construct() {
builder.name("Tom");
builder.age(12);
builder.hobby("BaseBall");
}
}
interface Builder {
void name(String name);
void age(Integer age);
void hobby(String hobby);
People getResult();
}
class PeopleBuilder implements Builder {
private People people;
PeopleBuilder() {
this.people = new People();
}
@Override
public void name(String name) {
people.setName(name);
}
@Override
public void age(Integer age) {
people.setAge(age);
}
@Override
public void hobby(String hobby) {
people.setHobby(hobby);
}
@Override
public People getResult() {
if (people.getName() == null || people.getAge() == null) {
throw new NullPointerException();
}
return this.people;
}
}
// Builder builder = new PeopleBuilder();
// Director director = new Director(builder);
// director.construct();
// builder.getResult().hello();
- 생성된 오브젝트(People)와 생성 로직(Builder)가 분리되어, People이 Builder를 몰라도 된다.
- Builder의 구현 클래스를 바꾸는 것으로 생성되는 People의 제약할 수 있다.
- interface이나 구현 등, 작성해야할 코드양이 많아진다.
- 생성처리가 다른 Builder 에 비해 복잡하다.
Effective Java Builder
Effective Java에서 소개된 Builder 패턴이다.
class People {
private String name;
private Integer age;
private String hobby;
static class Builder {
private String name;
private Integer age;
private String hobby;
Builder(String name, Integer age) {
this.name = name;
this.age = age;
}
Builder hobby(String hobby) {
this.hobby = hobby;
return this;
}
People build() {
if (name == null || age == null) {
throw new NullPointerException();
}
return new People(this);
}
}
private People(Builder builder) {
this.name = builder.name;
this.age = builder.age;
this.hobby = builder.hobby;
}
String hello() {
//...
}
}
// new People.Builder("Tom", 12).hobby("BaseBall").build().hello();
- 인터페이스로 오브젝트의 생성이 가능하다.
- GoF Builder 에 비해 적은 코드양으로 오브젝트를 생성할 수 있다.
- 생성 로직(Builder)와 오브젝트(People)의 분리가 불가능하다.
Lombok Builder
약간의 변화구이지만, Lombok이라는 라이브러리의 @Builder 어노테이션을 만드는 패턴이다.
@Builder
class People {
@NonNull
private String name;
@NonNull
private Integer age;
private String hobby;
String hello() {
//...
}
}
// People.builder().name("Tom").age(12).hobby("BaseBall").build().hello();
- 간략한 코드가 실현된다.
- @NonNull로 생성시에 예외를 발생시키는 것으로 필수 항목에 대해 구현할 수 있다.
- 생성 로직과 오브젝트의 분리를 할 수 없다.
Fluent Builder
Java로 의한 함수형 프로그래밍에 소개되어 있는 패턴. 이것은 엄격하게 builder 이라는 것보다 론 패턴.
class People {
private People() {
}
private String name;
private Integer age;
private String hobby;
People name(String name) {
this.name = name;
return this;
}
People age(Integer age) {
this.age = age;
return this;
}
People hobby(String hobby) {
this.hobby = hobby;
return this;
}
static String hello(final Consumer<People> builder) {
People people = new People();
builder.accept(people);
if (people.name == null || people.age == null) {
throw new NullPointerException();
}
//...
}
}
// Consumer<People> builder = people -> people.name("Tom").age(12).hobby("BaseBall");
// People.hello(builder);
- People 오브젝트의 스코프가 제약되어 hello 실행 종료하면 참조가 없어진다.
- 인터페이스로 오브젝트의 생성 로직을 기재할 수 있다.
- 생성 로직(Consumer <People>)의 전환이 쉽게 가능하다.
- Java8 환경이 아니면, 코드양도 가독성도 나빠진다.
참고자료
'IT > 언어' 카테고리의 다른 글
[Vue.js] props의 데이터를 변경하는 방법(Avoid mutating a prop directly에러) (0) | 2023.02.19 |
---|---|
[Java] setter, getter (0) | 2023.02.11 |
[JavaScript] Map 오브젝트와 map 메소드의 사용법 (0) | 2023.02.06 |
[JavaScript] Promise의 사용법 ( + asycn / await의 사용법 ) (0) | 2023.02.05 |
[JUnit] JUnit5의 테스트 클래스 작성 (0) | 2023.01.31 |