빌더 (Builder)
빌더 패턴은 복잡한 객체의 생성과 표현을 분리하여 인스턴스를 만드는 생성패턴입니다. 생성자에 들어갈 매개 변수를 메서드로 하나하나 받아들이고 마지막에 통합 빌드해서 객체를 생성하는 방식입니다.
빌더패턴을 사용하는 이유
우리는 데이터를 주입할 때 생성자를 이용하거나 기본생성자로 객체를 생성하고 이후에 Setter를 이용해서 값을 넣었습니다.
생성자를 이용한 주입
public class User {
private String name;
private int age;
private String sex;
private String phone;
private int height;
public User(String name, int age, String sex, String phone, int height) {
this.name = name;
this.age = age;
this.sex = sex;
this.phone = phone;
this.height = height;
}
}
public class Main {
public static void main(String[] args) {
User user = new User("김OO", 20, "남자", "01012345678", 70);
}
}
Setter를 이용한 주입
public class User {
private String name;
private int age;
private String sex;
private String phone;
private int height;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setSex(String sex) {
this.sex = sex;
}
public void setPhone(String phone) {
this.phone = phone;
}
public void setHeight(int height) {
this.height = height;
}
}
public class Main {
public static void main(String[] args) {
User user = new User();
user.setName("김OO");
user.setAge(20);
user.setSex("남자");
user.setPhone("01012345678");
user.setHeight(70);
}
}
이러한 방식은 생성자의 인자가 많아질 수록 데이터를 구별하기 어렵고, 필드와 생성자의 위치를 직관적으로 파악하기 어렵다는 것입니다. 그리고 타입이 많아질 수록 메서드 수가 기하급수적으로 늘어나 가독성과 유지보수 측면에서 안좋을 수 밖에 없습니다.
Setter는 객체를 생성하는 시점에 값을 주입하지않아 일관성(Consistency) 과 불변성(Immutable) 문제가 생길 수 있습니다.
생성자와 Setter의 문제점
일관성 문제
필수 매개변수란 객체가 초기화 될 때 반드시 설정되어야 하는 값입니다. 만약 개발자가 setAge() 메서드를 호출 하지 않고 User 객체를 생성했다면 이 객체는 일관성이 없는 상태가 됩니다. 만약 이 객체를 사용하게 된다면 예외가 발생할 수 있습니다.
불변성 문제
Setter의 문제는 객체를 생성한 이후에 값을 주입하는데요 이는 객체를 생성한 이후로도 누구나 외부에서 Setter를 사용해서 값을 주입할 수 있는 문제가 생깁니다. 다시 말해 누구든지 Setter를 호출해 객체를 조작할 수 있다는 뜻입니다. 따라서 Setter는 불변성이 보장되지않는 객체라고 할 수 있습니다.
Builder패턴의 사용
빌더 패턴은 이러한 문제들을 해결하기위해 Buillder 클래스를 만들어 생성되는 시점에 모든 값들을 넣어주도록 하는 패턴입니다.
// 생성자 주입
User user1 = new User("김OO", 20, "남자", "01012345678", 70);
// Setter 주입
User user2 = new User();
user.setName("김OO");
user.setAge(20);
user.setSex("남자");
user.setPhone("01012345678");
user.setHeight(70);
// Builder 주입
User user3 = User.builder()
.name("김OO")
.age(20)
.sex("남자")
.phone("01012345678")
.height(70)
.build();
빌더 패턴을 이용하면 생성자를 오버로딩을 하지 않아도되며, 데이터의 순서에 상관없이 데이터를 주입할 수 있게되었습니다.
빌더패턴의 장점
- 필요한 데이터만 설정할 수 있습니다.
- 가독성이 좋습니다.
- 변경 가능성을 최소화 할 수 있습니다.
빌더패턴의 구조
구현자체는 어렵지 않습니다. 위의 User 객체에 Builder 패턴을 구현해보겠습니다.
1. builder()
User 클래스 안에 Builder 클래스를 구현해줍니다.
public class User {
private String name;
private int age;
private String sex;
private String phone;
private int height;
public static UserBuilder builder() {
return new UserBuilder();
}
public static class UserBuilder {
}
}
User.builder()로 Builder 패턴을 시작해야하기 때문에 static 을 사용해 클래스를 만들어줍니다.
2. Chaning
public static class UserBuilder {
private final User user;
public UserBuilder() {
this.user = new User();
}
public UserBuilder name(String name) {
user.name = name;
return this;
}
public UserBuilder age(int age) {
user.age = age;
return this;
}
public UserBuilder sex(String sex) {
user.sex = sex;
return this;
}
public UserBuilder phone(String phone) {
user.phone = phone;
return this;
}
public UserBuilder height(int height) {
user.height = height;
return this;
}
public User build() {
return user;
}
}
User.builder() 를 입력하면 UserBuilder 기본생성자를 통해 User 기본생성자를 주입시켜줍니다. 여기서 주의할 사항은 User의 기본생성자가 존재해야합니다. 인자를 받는 생성자가 존재한다면 기본생성자를 하나 더 만들어주어야합니다.
이후에 반환타입은 UserBuilder, 메서드명은 User의 변수명으로 만들어 메서드를 체이닝(Chaining) 형태로 호출하고 마지막에 build()를 통해 최종적으로 객체를 반환하도록 만들어주면 Builder 패턴을 쉽게 구현할 수 있습니다.
'디자인패턴 > 생성' 카테고리의 다른 글
자바(JAVA) - 싱글톤 패턴(Singleton Pattern) (0) | 2024.02.23 |
---|---|
자바(JAVA) - 프로토타입 패턴(Prototype Pattern) (0) | 2024.02.01 |
자바(JAVA) - 팩토리 메소드 패턴(Factory Method Pattern) (0) | 2024.01.30 |
자바(JAVA) - 추상 팩토리 패턴(Abstract Factory Pattern) (0) | 2024.01.22 |