Builder Pattern
볡μ‘ν κ°μ²΄μ λν΄ μμ±(contruction)κ³Ό νν(representation)μ λΆλ¦¬
ν¨μΌλ‘μ¨ λκ°μ μμ± κ³Όμ μΌλ‘ μλ‘ λ€λ₯Έ κ°μ²΄ ννμ κ°λ₯νκ² νλ μμ± λμμΈ ν¨ν΄
Builder ν¨ν΄μ μ¬μ©ν΄μΌνλ μ΄μ
Immutability
- κ°μ²΄μ λΆλ³μ±μ μ μ§ν μ μμNamed Parameter with Chaining
- 체μ΄λμ ν΅ν λͺ λͺ λ 맀κ°λ³μ μ¬μ©μΌλ‘ κ°λ μ± μ¦μ§Design Flexibility
- νμμ μΈ λ³μμ μ νμ μΈ λ³μλ₯Ό κ°κ° μμ± κ°λ₯Easy Maintenance
- μλ‘μ΄ λ©€λ²κ° μΆκ°λλλΌλ κΈ°μ‘΄μ κ°μ²΄ μμ± μ½λλ₯Ό μμ ν νμ μμAvoid RuntimeException
- κ°μ²΄ μμ± κ³Όμ μμ μ ν¨μ± κ²μ¬λ₯Ό ν΅ν΄ λ Όλ¦¬μ μΈ μλ¬λ₯Ό λ§μ μ μμ
π‘ λΆλ³μ μΈ κ°μ²΄λ‘ ꡬνν΄μΌνλ μ΄μ
- λΆλ³μ±(
Immutability
)μ΄λ? κ°μ²΄κ° μ΄κΈ°μ νλ² μμ±λ μ΄νμλ μ λ μνλ₯Ό λ°κΎΈμ§ μλ κ²μ λ§νλ€. κ°μ²΄ μμ±μμ λͺ¨λ μ λ³΄κ° μ£Όμ΄μ§κ³ κ°μ²΄μ μμ μ£ΌκΈ° λμμλ μνκ° λ°λμ§ μλ κ²μ΄ νΉμ§μ΄λ€.- μ¬μ©μ΄ μ½λ€
- Thread Safe νλ€. λκΈ°νν νμκ° μλ€.
- μμ λ‘κ² κ³΅μ ν μ μλ€.
Builder ν¨ν΄μ νκ³
μ½λλ₯Ό 2λ°°μ λ λ§μ΄ μ¬μ©νκ² λλ€. λ°λΌμ μ€μ ν΄μΌ ν 맀κ°λ³μκ° μ μ κ²½μ°μλ μΌλ° μμ±μλ₯Ό ν΅ν μμ±μ΄ λμ± νΈν μλ μλ€.
ꡬν
Builder ν¨ν΄ μ μ© μ
μΌλ°μ μΌλ‘λ μμ±μ(Constructor)λ₯Ό ν΅ν΄ κ°μ²΄λ₯Ό μμ±ν κ²μ΄λ€. μμ±μλ₯Ό μ¬μ©ν κ²½μ° λ©€λ²λ₯Ό μ νμ μΌλ‘ μμ±νκΈ° μ΄λ ΅λ€.
1. μμ±μλ₯Ό μ¬μ©ν μμ± - μλ°λΉμ¦ ν¨ν΄(JavaBeans Pattern)
맀κ°λ³μκ° μλ κΈ°λ³Έ μμ±μλ₯Ό ν΅ν΄ κ°μ²΄λ₯Ό μμ±ν λ€, Setter λ©μλλ₯Ό ν΅ν΄ λ©€λ²λ₯Ό μ€μ νλ λ°©μμ΄λ€.
[ν΄λμ€ μ μ]
public User() {
}
public User(String firstName, String lastName, int age, String phone, String address) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.phone = phone;
this.address = address;
}
[κ°μ²΄ μμ±]
User user1 = new User("ssafy", "Kim", 6, "02-666-6666", "μμΈμ κ°λ¨κ΅¬ ν
ν€λλ‘ 212 λ©ν°μΊ νΌμ€");
User user2 = new User("ssafy", "Lee", 5, null, null);
User user3 = new User();
user3.setFirstName("ssafy");
user3.setLastName("Choi");
2. μμ±μλ₯Ό μ¬μ©ν μμ± - μ μΈ΅μ μμ±μ ν¨ν΄(Telescoping Constructor Pattern)
νμ 맀κ°λ³μλ§μ κ°μ§ μμ±μλ₯Ό λ§λ€κ³ μ ν 맀κ°λ³μλ₯Ό νλμ© μΆκ°ν μμ±μλ₯Ό λ§λ λ€. μλ§μ μμ±μ μ€λ²λ‘λ©μ ν΅ν΄ μνλ ννμ κ°μ²΄λ₯Ό μμ±νλλ‘ νλ λ°©μμ΄λ€.
[κ°μ²΄ μμ±]
public User (String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.phone = null;
this.address = null;
}
public User (String firstName, String lastName, int phone) {
this.firstName = firstName;
this.lastName = lastName;
this.age = null;
this.phone = phone;
this.address = null;
}
μ μμ μ κ°μ΄ μμ±μλ₯Ό ν΅ν κ°μ²΄ μμ± μ νμ 맀κ°λ³μμ μ ν 맀κ°λ³μλ₯Ό ꡬλΆνμ¬ κ΅¬ννκΈ°κ° μ΄λ ΅λ€. νΉν 맀κ°λ³μκ° λ§μμ§λ€λ©΄ μΌμΌν setterλ₯Ό λΆλ₯΄λ μΌλ, 맀κ°λ³μ μ리λ₯Ό μΈμ£Όλ κ²λ μΌμ΄λ€. μμ±μλ₯Ό κ²½μ°μ μ λ³λ‘ ꡬννλ κ²μ λμ± λμ°ν κ²μ΄λ€.
Builder ν¨ν΄ μ μ© ν
[ν΄λμ€ μ μ]
// ν΄λμ€λ₯Ό Finalλ‘ μ€μ νμ¬ νμ₯μ΄ λΆκ°λ₯νλ©° λΆλ³μ±μ΄ μ μ§λ¨
public final class User
{
// λΆλ³μ±μ μ μ§νκΈ° μν΄ private finalλ‘ μ€μ
private final String firstName; // νμ λ³μ
private final String lastName; // νμ λ³μ
private final int age; // μ ν λ³μ
private final String phone; // μ ν λ³μ
private final String address; // μ ν λ³μ
private User(UserBuilder builder) {
this.firstName = builder.firstName;
this.lastName = builder.lastName;
this.age = builder.age;
this.phone = builder.phone;
this.address = builder.address;
}
// Setterλ₯Ό ꡬννμ§ μμμΌλ‘μ¨ λΆλ³μ± μ μ§
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int getAge() {
return age;
}
public String getPhone() {
return phone;
}
public String getAddress() {
return address;
}
@Override
public String toString() {
return "User: "+this.firstName+", "+this.lastName+", "+this.age+", "+this.phone+", "+this.address;
}
// κ°μ²΄ λ΄λΆμ Builder μ μ(μ€μ²© ν΄λμ€)
public static class UserBuilder
{
// νμμ μΈ λ³μλ§ finalλ‘ μ€μ
private final String firstName; // νμ λ³μ
private final String lastName; // νμ λ³μ
private int age; // μ ν λ³μ
private String phone; // μ ν λ³μ
private String address; // μ ν λ³μ
// Builder μμ±μ 맀κ°λ³μλ νμ λ³μλ§μ ν¬ν¨
public UserBuilder(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
// μ νμ μΈ λ³μλ μΆκ°μ μΈ λ©μλλ₯Ό ꡬννμ¬ μμ±
public UserBuilder age(int age) {
this.age = age;
return this;
}
public UserBuilder phone(String phone) {
this.phone = phone;
return this;
}
public UserBuilder address(String address) {
this.address = address;
return this;
}
// Builderλ‘ μμ±λ κ°μ²΄ λ°ν
public User build() {
User user = new User(this);
if (!validateUserName(user)) throw new NoNameException();
if (!validateUserAge(user)) throw new InvalidAgeException();
return user;
}
private boolean validateUserName(User user) {
if (user.firstName==null || user.lastName==null) {
if (user.age!=null || user.phone!=null || user.address!=null) return false;
}
return true;
}
private boolean validateUserAge(User user) {
if (user.age<0) return false;
return true;
}
}
}
[κ°μ²΄ μμ±]
User user1 = new User.UserBuilder("ssafy", "Kim")
.age(6)
.phone("02-666-6666")
.address("μμΈμ κ°λ¨κ΅¬ ν
ν€λλ‘ 212 λ©ν°μΊ νΌμ€")
.build();
User user2 = new User.UserBuilder("ssafy", "Lee")
.age(5)
// no phone
// no address
.build();
User user3 = new User.UserBuilder("ssafy", "Choi")
// no age
// no phone
// no address
.build();
λΉλ ν¨ν΄μ μ΄μ©ν΄ μμ κ°μ΄ νλμ μμ±μλ§μΌλ‘ μ¬λ¬ μνμ κ°μ²΄λ₯Ό μμ±ν μ μκ²λμλ€.
- λ©€λ²λ₯Ό
final
λ‘ μ€μ νμ¬ λΆλ³μ±μ μ μ§ν μ μλ€. - μ νμ μΈ λ³μμ κ²½μ°
null
λ‘ μ€μ ν νμκ° μλ€. λν μμ±μ μ€λ²λ‘λ©μ νμ§ μμ μ νμ μΈ λ³μλ₯Ό κ°μ§ κ°μ²΄λ λμΌν λ°©λ²μΌλ‘ μμ±ν μ μλ€. - κ° λ³μμ μ΄λ¦μ ν΄λΉνλ λ©μλλ₯Ό chanining λ°©μμΌλ‘ μ κ·Όνμ¬ μ΄κΈ°νν μ μλ€. λ°λΌμ μμ±μμ 맀κ°λ³μ μμλ₯Ό κΈ°μ΅ν νμκ° μκ³ , μμ± κ³Όμ μμμ κ°λ μ±μ΄ ν¨μ¬ μ’μμ§λ€.
- λ§μ½ μλ‘μ΄ λ©€λ² λ³μκ° μΆκ°λλλΌλ κΈ°μ‘΄ κ°μ²΄ μμ± μ½λλ₯Ό μμ νμ§ μμλ λλ€. μλ‘κ² μΆκ°λ λ©€λ² λ³μλ μ νμ μΈ λ§€κ°λ³μμ λμΌνκ² μ²λ¦¬νκΈ° λλ¬Έμ΄λ€.
- μΆκ°μ μΌλ‘ λΉλ ν΄λμ€ λ΄λΆμ μ ν¨μ± κ²μ¬ λ©μλλ₯Ό μΆκ°νλ€λ©΄ λ©€λ² μμ± κ³Όμ μμμ λ Όλ¦¬μ μΈ μλ¬λ₯Ό μ¬μ μ μ°¨λ¨ν μ μλ€.
References
dzone Immutability and Builder Pattern