[목차]
1. Builder Pattern - 빌더 패턴이란?
2. Builder Pattern은 왜 사용하는 것일까?
2-1. 점층적 생성자 패턴
2-2. 자바빈 패턴
2-3. Builder Pattern의 단점
3. 구현을 보기전에 알아두면 좋은 부분
4. 빌더 패턴의 구현 방법
5. 결론
| Builder Pattern - 빌더 패턴이란?
- 빌더 패턴은 생성 패턴(Credential Pattern)중 하나입니다.
- 빌더 패턴은 객체의 생성을 유연하게 해주는 패턴입니다.
- 빌드 패턴은 객체의 생성과정과 객체의 표현 방법을 분리합니다.
| Builder Pattern은 왜 사용하는 것일까?
- 빌더 패턴을 이용하면 가독성이 이전보다 좋아지며 인자의 의미를 코드에서 명확히 알 수 있습니다.
- setter() 메소드가 없으므로 변경이 불가능한 객체를 만들 수 있습니다.
- 한 번에 객체를 반환하므로 객체의 일관성이 깨지지 않습니다.
- 간편하게 사용할 수 있으며 필수 인자를 지정할 수 있습니다.
- 추가적인 이유에 대하여 아래 코드 예시를 확인하면서 보여드리겠습니다.
- Person 클래스
public class Person {
private String name;
private int age;
private String job;
private String gender;
private String country;
public Person(String name, int age, String job, String gender, String country) {
this.name = name;
this.age = age;
this.job = job;
this.gender = gender;
this.country = country;
}
public static void main(String[] args) {
Person person = new Person("Lee", 25, "Student", "Man", "Korea");
}
}
- Person 인스턴스를 생성하려면 메인 메소드의 코드처럼 작성하여 생성자를 통해 만들 수 있습니다. 만약에 빈 값, 입력하지 않을 값이 있다면, null을 사용하면 됩니다.
Person person = new Person("Lee", 25, null, null, null);
- 하지만, 이렇게 작성할 경우 생성자에 전달된 인자 값이 어떠한 필드의 값인지 정확히 알 수 없습니다. 이를 해결하고자, 생성자를 오버로딩 할 수 있습니다. 이는 곧 점층적 생성자 패턴이라고 합니다.
점층적 생성자 패턴(Telescoping Constructor Pattern)
public Person() {
this("", 0, "", "", "");
}
public Person(String name) {
this(name, 0, "", "", "");
}
public Person(String name, int age) {
this(name, age, "", "", "");
}
public Person(String name, int age, String job) {
this(name, age, job, "", "");
}
public Person(String name, int age, String job, String gender) {
this(name, age, job, gender, "");
}
- 하지만 여전히 가독성은 좋지 않으며 기존의 코드에서 다른 인자를 추가할 때 코드를 수정하기 어렵습니다.
- 따라서, 이러한 부분을 보완하기 위해 자바빈 패턴이 등장했습니다.
자바빈 패턴(JavaBeans Pattern)
public void setName(String name) {
this.name = name;
}
public void setJob(String job) {
this.job = job;
}
public void setAge(int age) {
this.age = age;
}
public void setGender(String gender) {
this.gender = gender;
}
public void setCountry(String country) {
this.country = country;
}
public static void main(String[] args) {
Person person = new Person();
person.setName("Lee");
person.setCountry("Korea");
}
- 이렇게 구현하면 한번의 생성자 호출로 객체 구현을 완성하지 못하며 일관성이 없습니다. 더불어 불변의 특징을 가진 객체를 만들 수 없습니다.
- 그래서 점층적 생성자 패턴과 자바빈 패턴의 장점을 결합한 것이 빌더 패턴입니다. 위의 코드에 빌더 패턴을 적용한 예시는 아래 구현 파트에서 확인하도록 하겠습니다.
[Builder Pattern의 단점]
- 추가적인 빌더 클래스를 구현해야 합니다.
- 빌더의 생성 비용이 크지는 않지만, 성능이 민감할 경우 문제가 될 수 있습니다.(?)
| 여기서 잠깐! 구현 방법을 보기 전에 이것만큼은 간략히 알고 가자!
1. 롬복(Lombok)의 @Builder
- 롬복(Lombok)이란?
- 먼저, 롬복은 반복적인 메소드 코드 작성을 줄여주는 Java 라이브러리입니다. 우리는 어노테이션을 사용하여 이용할 수 있습니다.
- 예로 들어, @Setter 를 사용하였다면 해당 클래스의 필드를 이용하여 setter() 메소드가 자동으로 만들어집니다.
- 당연히, @Builder 어노테이션을 사용한다면 잠시 후, 작성할 코드에 대해서도 하나씩 작성할 필요가 없게 되는 것입니다. @Builder 어노테이션을 사용하기 전과, 사용했을 때의 코드도 비교해보도록 하겠습니다!
| 빌더 패턴의 구현 방법
- 위의 Person 클래스 예시를 이용해서 구현해보도록 하겠습니다.
- Person 클래스 내부에 정적 클래스로 Builder 클래스를 만듭니다. 이후 build() 메소드를 통해서 새로운 Person() 객체를 생성합니다.
public class Person {
private String name;
private int age;
private String job;
private String gender;
private String country;
Person(Builder builder) {
}
public static class Builder {
private String name;
private int age;
private String job;
private String gender;
private String country;
public Builder(String name) {
this.name = name;
}
public Builder age(int age) {
this.age = age;
return this;
}
public Builder job(String job) {
this.job = job;
return this;
}
public Builder gender(String gender) {
this.gender = gender;
return this;
}
public Builder country(String country) {
this.country = country;
return this;
}
public Person build() {
return new Person(this);
}
}
}
public static void main(String[] args) {
Person person = new Builder("Lee")
.age(25)
.country("Korea")
.gender("Man")
.job("Student")
.build();
}
- 이처럼, 보다 유연하고 가독성이 높게 인스턴스를 만들 수 있습니다.
- 또한, 위의 예시에서는 직접 빌더 클래스를 작성하였지만 롬복(Lombok)의 @Builder 어노테이션을 활용하면 직접 구현하지 않고 편리하게 사용할 수 있습니다.
- 앞서, 배운 롬복(Lombok)을 활용하면 코드가 어떻게 되는지 확인해보겠습니다.
import lombok.Builder;
@Builder
public class Person {
private String name;
private int age;
private String job;
private String gender;
private String country;
public static void main(String[] args) {
Person person = Person.builder()
.name("Lee")
.age(25)
.country("Korea")
.gender("Man")
.job("Student")
.build();
}
}
- 보시는 것처럼 Person 클래스 내부의 Builder 클래스를 작성하지 않아도 되기 때문에 코드가 훨씬 간단해지고 가독성이 좋게 되었습니다.
- 그렇다면, 롬복의 Builder 어노테이션을 사용하면 어떻게 필수 인자를 지정하는가? 라고 생각하실 수 있습니다. 현재, 위의 예시에서 만약 값을 할당하지 않을 경우 null값이 반환되는데 이는 추후 문제를 발생시킬 수 있습니다. 그렇기 때문에 사전에 @NonNull 어노테이션을 이용하거나 Assert를 이용하여 사전에 방지하는 것이 좋습니다.
| Conclusion
Builder Pattern이란?
- 객체를 직접적으로 참조하는 것이 아니라, 여러가지의 목적으로 구현된 대리역할의 객체를 통해 기능을 수행하는 것을 의미합니다.
왜 Builder Pattern을 사용하는가?
- 가독성이 보다 좋아집니다.
- 불변성의 객체를 만들 수 있습니다.
- 간편하게 사용할 수 있으며 필수 인자를 지정할 수 있습니다.
Builder Pattern을 공부하며
- 빌더 패턴은 이전부터 알고 있었지만, 어떻게 시작되었는지 구체적으로 어떠한 장점이 있는지는 모르고 사용했습니다. 그저 간편하다는 생각으로 사용했지만 이번에 공부하면서 여러 장점을 알 수 있었으며 자바 빈즈 패턴과 점층적 생성자 패턴에 대해서도 공부할 수 있었습니다.
(참고)
'Develop > JAVA' 카테고리의 다른 글
Java 디자인 패턴 다섯번째 이야기 - 전략 패턴(Strategy Pattern) (0) | 2021.08.16 |
---|---|
Java 디자인 패턴 네번째 이야기 - 팩토리 메소드 패턴(Factory Method Pattern) (0) | 2021.08.16 |
Java 디자인 패턴 두번째 이야기 - 프록시 패턴(Proxy Pattern) (0) | 2021.08.10 |
Java 디자인 패턴 첫번째 이야기 - 싱글톤 패턴(Singleton Pattern) (0) | 2021.08.07 |
Object 클래스의대표적인 메소드(equals(),hashcode(),toString()) (0) | 2021.08.04 |