🔍 Builder Pattern
- 자바의 여러가지 패턴 종류 중 하나
- 인스턴스 생성 시 멤버변수의 갯수가 많아질 경우 다양한 방식으로
객체 생성을 위해서 생성자 오버로딩이 필요한데 생성자 오버로딩으로는 한계가 있다.
- 필요한 멤버변수만 초기화 하면서 생성하고 싶은 경우에 빌더패턴을 사용한다.
- Spring(Spring boot 포함) 에서 @Builder 어노테이션만 사용하면
해당클래스는 Builder 패턴으로 객체를 생성할 수 있다.
- 특히 안드로이드는 이미 제공해주는 클래스로 객체를 생성할 때 Buidler 패턴이 자주 보인다.
✍ 생성자 오버로딩의 한계
public class test11 {
public static void main(String[] args) {
new NormalPerson("홍길동", "111-1111", 20);
new NormalPerson("222-2222", "이순신", 30);
}
}
class NormalPerson {
// 멤버변수
private String name;
private String jumin;
private int age;
public NormalPerson(String name, String jumin, int age) { // 생성자
this.name = name;
this.jumin = jumin;
this.age = age;
}
}
자, 이 코드를 보면
new NormalPerson(); 생성자에서 어떤 멤버변수가 초기화 되었는지 알 수 있을까?
name과 jumin 모두 String 타입이기 떄문에, 순서가 맞지 않다하더라고 멤버변수의 값이 생성될 가능성이 존재한다.
그렇다면 Setter 메서드를 활용하여 필요한 멤버변수만 초기화 하는 것을 어떨까?
class SetterPerson {
private String name; // 멤버변수
private String jumin; // 멤버변수
private int age; // 멤버변수
// Setter 메서드
public void setName(String name) {
this.name = name;
}
public void setJumin(String jumin) {
this.jumin = jumin;
}
public void setAge(int age) {
this.age = age;
}
}
이렇게 Setter 메서드를 만들어주면
public static void main(String[] args) {
SetterPerson sp = new SetterPerson(); // 인스턴스 생성
// setter 메서드를 통해 초기화
sp.setName("홍길동");
sp.setJumin("111-1111");
sp.setAge(20);
}
내가 원하는 멤버변수만 골라서 초기화를 할 수 있다.
그럼 괜찮지 않을까? 싶지만 !!
setter를 활용할 때도 문제점이 존재한다.
- 한번의 생성자로 멤버변수를 초기화할 수 없다. (즉, 생성 후 데이터를 초기화 하고 있다는 말이다)
- 불변의 객체를 만들 수 없다.
=> 파라미터로 해당 객체를 전달 후 객체를 조작하면 참조변수 즉, 주소를 전달했으므로 변경된 데이터로 동작할 것이다
예를 들어
Person p = new Person("이순신");
p.method(); ( => name이 이순신인 상태)
p.setName("홍길동") (=> name을 "홍길동"으로 조작)
p.method(); ( <= name 이 홍길동인 상태로 메서드 호출)
이렇게 객체를 조작할 가능성이 생긴다.
그럼 이제 Builder 패턴을 살펴보자
class BuilderPerson {
private String name;
private String jumin;
private int age;
private BuilderPerson(Builder builder) { // Builder 생성자
this.name = builder.name;
this.jumin = builder.jumin;
this.age = builder.age;
}
static class Builder {
private String name;
private String jumin;
private int age;
public Builder(String jumin) { // 필수 데이터가 필요하다면 파라미터 생성자 정의
this.jumin = jumin;
}
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setAge(int age) {
this.age = age;
return this;
}
public BuilderPerson build() {
return new BuilderPerson(this);
}
}
}
메인메서드에서
public static void main(String[] args) {
BuilderPerson bp = new BuilderPerson.Builder("111-1111").setAge(20).build();
}
=> 이렇게 원하는 멤버변수만 초기화 할 수 있다.
Builder 패턴을 사용할 경우
new BuilderPerson을 직접 생성할 방법이 사라진다. 왜냐?
생성자가
private BuilderPerson(Builder builder) { // Builder 생성자
this.name = builder.name;
this.jumin = builder.jumin;
this.age = builder.age;
}
이렇게 private이기 때문이다. (외부에서 접근 불가)
또한 모든 멤버변수도 private 접근제한자로 인해 외부에서 조작할 수 없다
지금은 멤버변수가 3개 뿐이지만 만약 50개, 100개로 늘어날 경우 이 빌더패턴이 매우 유용해지게 된다.
빌더패턴의 제일 마지막에 BuilderPerson을 생성하여 리턴한다. 그래서 메인메서드 코드 제일 뒤에
.build()를 호출하여 리턴하는 것이다.
'개발 > Java' 카테고리의 다른 글
[Java] Collection Framework - Set (0) | 2023.03.04 |
---|---|
[Java] Object (0) | 2023.03.04 |
[Java] 중첩 클래스 (Nested Class) (0) | 2023.03.01 |
[Java] 익명 클래스 (0) | 2023.03.01 |
[Java] 사용자 정의 예외 (0) | 2023.03.01 |