Effective Java 3/E
์ ์ ํฉํฐ๋ฆฌ ๋ฉ์๋์ public ์์ฑ์๋ ๊ฐ์์ ์ฐ์์๊ฐ ์์ผ๋ ์๋์ ์ธ ์ฅ๋จ์ ์ ์ดํดํ๊ณ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๊ฒ ๋ค. ๊ทธ๋ ๋ค๊ณ ํ๋๋ผ๋ ์ ์ ํฉํฐ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋๊ฒ ์ ๋ฆฌํ ๊ฒฝ์ฐ๊ฐ ๋ ๋ง์ผ๋ฏ๋ก ๋ฌด์์ public ์์ฑ์๋ฅผ ์ ๊ณตํ๋ ์ต๊ด์ด ์๋ค๋ฉด ๊ณ ์น์!
์์ดํ 2: ์์ฑ์์ ๋งค๊ฐ๋ณ์๊ฐ ๋ง๋ค๋ฉด ๋น๋๋ฅผ ๊ณ ๋ คํ๋ผ
Class ์ค๊ณ์ ํ์, ์ ํ์ ์ผ๋ก ๋ฐ์ ๋งค๊ฐ๋ณ์๊ฐ ๊ตฌ๋ถ๋ฉ๋๋ค.
์ด๋ ๊ฒ ์ค๊ณ๋ ๋คํฅํ ํํ์ ํด๋์ค๋ค์ ๊ฐ์ฒดํํ๋ ๋ํ์ ์ธ 3๊ฐ์ง ํจํด์ด ์กด์ฌํฉ๋๋ค.
1. ์ ์ธต์ ์์ฑ์ ํจํด ( Telescoping Constructor Pattern )
2. ์๋ฐ ๋น์ฆ ํจํด ( Java Beans Pattern )
3. ๋น๋ ํจํด ( Builder Pattern )
1. ์ ์ธต์ ์์ฑ์ ํจํด ( Telescoping constructor pattern )
ํ์ ๋งค๊ฐ๋ณ์๋ง ๋ฐ๋ ์์ฑ์๋ถํฐ ์ถ๊ฐ์ ์ผ๋ก ์ ํ ๋งค๊ฐ๋ณ์๋ฅผ 1๊ฐ, 2๊ฐ... N๊ฐ ํํ๋ก ์ ํ ๋งค๊ฐ๋ณ์๋ฅผ ์ ๋ถ ๋ฐ๋ ์์ฑ์๊น์ง ๋๋ ค๊ฐ๋ ๋ฐฉ์์ด๋ฉฐ, ๋ง์น ์์ฑ์๊ฐ ์ ์ธต์ ์ผ๋ก ๋ง์์ง๋ ์์ฑ์๋ฅผ ๊ฐ์ง๋๋ก ํ ๋์์ธ ํจํด์ ๋๋ค.
public class Member {
private String name; // ํ์
private int age; // ํ์
private String address; // ์ ํ
private String phone; // ์ ํ
private String email; // ์ ํ
// ํ์ ๋งค๊ฐ๋ณ์๋ฅผ ๊ฐ์ง๋ ์์ฑ์
public Member(String name, int age) {
this(name, age, null, null, null);
}
// ์ ํ ๋งค๊ฐ๋ณ์ address๊ฐ ์ถ๊ฐ๋ ์์ฑ์
public Member(String name, int age, String address) {
this(name, age, address, null, null);
}
// ์ ํ ๋งค๊ฐ๋ณ์ phone์ด ์ถ๊ฐ๋ ์์ฑ์
public Member(String name, int age, String address, String phone) {
this(name, age, address, phone, null);
}
// ๋ชจ๋ ๋งค๊ฐ๋ณ์๋ฅผ ๊ฐ์ง๋ ์์ฑ์
public Member(String name, int age, String address, String phone, String email) {
this.name = name;
this.age = age;
this.address = address;
this.phone = phone;
this.email = email;
}
}
์ ์ธต์ ์์ฑ์ ํจํด์ ๋จ์
- ๋งค๊ฐ๋ณ์๊ฐ ๋ง์์ง์๋ก ๋ง์ ์กฐํฉ์ด ๋ง๋ค์ด์ง๊ณ , ์์ฑ์์ ์๊ฐ ๋ง์์ง๋๋ค. ์ด๋ ์ฝ๋ ์์ฑ ํจ์จ๊ณผ ๊ฐ๋ ์ฑ์ด ์ ํ๋๋ ์ํฅ์ ์ค๋๋ค.
- ํด๋์ค์ ์์ฑ์๋ฅผ ํธ์ถํ๋ ์ ์ฅ์์ ํด๋น ๋งค๊ฐ๋ณ์๊ฐ ๋ง๋์ง, ๋งค๊ฐ๋ณ์์ ๊ฐ์๋ ์ ๋๋ก ์ ๋ ฅํ ๊ฒ์ธ์ง ํ์ธํด์ผ ํ๋ ๋ถํธํจ์ด ์์ต๋๋ค.
- ๋งค๊ฐ๋ณ์์ ํ์ ์ด ๊ฐ์ ๊ฒฝ์ฐ ์์ฑ์๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค.
Member member = new Member("jan", 99, "KOR", "01012345678");
Test test = new Test(12, 10, 1, 330, 9);
Member์ ๊ฒฝ์ฐ ๊ฐ๋จํ ์์ฑ์๋ผ์ ๋ค์ด๊ฐ๋ ๊ฐ๋ง ๋ด๋ ์ด๋ค ๋ด์ฉ์ธ์ง ์ ์ถํ ์ ์์ต๋๋ค.
ํ์ง๋ง ์๋ฅผ ๋ค์ด Test ๊ฐ์ ์์ฑ์์ ์ธ์๋ก ๋ชจ๋ int ๊ฐ๋ง ๋ค์ด๊ฐ๋ ๊ฒฝ์ฐ๊ฐ ์๋ค๋ฉด 12๊ฐ ๋ฌด์จ ๊ฐ์ธ์ง, 330์ด ๋ฌด์จ ๊ฐ์ธ์ง ์๊ธฐ ์ํด์๋ ์์ฑ์๋ฅผ ์ฐพ์๋ด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ์๊น๋๋ค.
// ์ ํ ๋งค๊ฐ๋ณ์ address๊ฐ ์ถ๊ฐ๋ ์์ฑ์
public Member(String name, int age, String address) {
this(name, age, address, null, null);
}
// ์ ํ ๋งค๊ฐ๋ณ์ phone์ด ์ถ๊ฐ๋ ์์ฑ์
public Member(String name, int age, String phone) {
this(name, age, null, phone, null);
}
๋ง์ง๋ง ๋งค๊ฐ๋ณ์ ํ์ ์ด ๊ฐ์ ์์ฑ์๋ฅผ ๋ง๋ค ์ ์๋ ๊ฒฝ์ฐ์ ๋๋ค. ๋ค์๊ณผ ๊ฐ์ ์์ฑ์๋ ์ธ์๋ก ๋ฐ๋ ํ์ ์ด ๊ฐ๊ธฐ ๋๋ฌธ์ ์ด๋ ๊ฒ ๋ ๊ฐ์ ์์ฑ์๋ฅผ ๋์์ ์์ฑํ ์ ์์ต๋๋ค.
2. ์๋ฐ ๋น์ฆ ํจํด ( Java Beans Pattern )
๋งค๊ฐ๋ณ์๊ฐ ์๋ ์์ฑ์๋ก ๊ฐ์ฒด๋ฅผ ๋ง๋ ํ Setter ๋ฉ์๋๋ฅผ ํธ์ถํด ์ํ๋ ๋งค๊ฐ๋ณ์์ ๊ฐ์ ์ค์ ํ๋ ๋ฐฉ์์ ํจํด์ด๋ค.
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setAddress(String address) {
this.address = address;
}
public void setPhone(String phone) {
this.phone = phone;
}
public void setEmail(String email) {
this.email = email;
}
Member member = new Member();
member.setName("jan");
member.setAge(99);
member.setAddress("KOR");
member.setPhone("01012345678");
์๋ฐ๋น์ฆ ํจํด์ ์ฅ๋จ์
์ฝ๋๊ฐ ๊ธธ์ง๋ง ์ธ์คํด์ค๋ฅผ ๋ง๋ค๊ธฐ ์ฝ๊ณ , ๊ฐ๋ ์ฑ์ด ์ข๋ค๋ ์ฅ์ ์ด ์์ต๋๋ค.
๋จ์ ์ผ๋ก๋ ํ๋์ ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ธฐ๊น์ง ๋ฉ์๋๋ฅผ ์ฌ๋ฌ ๋ฒ ํธ์ถํด์ผ ํ๊ณ , setter ๋ฉ์๋๊ฐ ์๊ธฐ ๋๋ฌธ์ ์ผ๊ด์ฑ์ด ๋ฌด๋์ง ์ํ.
์์ ์ ์ธต์ ์์ฑ์ ํจํด์์๋ ๋งค๊ฐ๋ณ์๋ค์ด ์ ํจํ์ง ์์ฑ์์์๋ง ํ์ธํ๋ฉด ์ผ๊ด์ฑ ์ ์ง๊ฐ ๊ฐ๋ฅํ์ง๋ง, ์๋ฐ๋น์ฆ ํจํด์ ๊ฒฝ์ฐ์๋ ๋ถ๊ฐ๋ฅํ๋ค. ๋๋ฌธ์ ๋ถ๋ณ์ ๊ฐ์ฒด(immutable)๋ฅผ ๋ง๋ค ์ ์๊ณ , ์ฐ๋ ๋ ์์ ์ฑ์ ์ํด ์ถ๊ฐ์ ์์ (์๋์ผ๋ก freeze)์ด ํ์ํ๋ค.
3. ๋น๋ ํจํด ( Builder Pattern )
์ ์ธต์ ์์ฑ์ ํจํด์ ์์ ์ฑ + ์๋ฐ๋น์ฆ ํจํด์ ๊ฐ๋ ์ฑ ๋๊ฐ์ง ํจํด์ ์ฅ์ ์ ๊ฐ์ง ํจํด์ด๋ค.
public class Member {
private String name;
private int age;
private String address;
private String phone;
private Member(Builder builder) {
this.name = builder.name;
this.age = builder.age;
this.address = builder.address;
this.phone = builder.phone;
}
// ๋น๋ ํธ์ถ, ์ธ๋ถ์์ Member.builder() ์ผ๋ก ์ ๊ทผํ ์ ์๋๋ก static ๋ฉ์๋๋ก ์์ฑ
public static Builder builder() {
return new Builder();
}
// static ํํ์ inner class ์์ฑ
public static class Builder {
private String name;
private int age;
private String address;
private String phone;
private Builder() {};
public Builder name(String name) {
this.name = name;
return this;
}
public Builder age(int age) {
this.age = age;
return this;
}
public Builder address(String address) {
this.address = address;
return this;
}
public Builder phone(String phone) {
this.phone = phone;
return this;
}
// ๋ง์ง๋ง์ build ๋ฉ์๋๋ฅผ ์คํํ๋ฉด this๊ฐ return ๋๋๋ก ๊ตฌํ
public Member build() {
return new Member(this);
}
}
}
Build Pattern์ ์ฌ์ฉํ ํด๋์ค๋ ์์ ๊ฐ์ด ๊ตฌํํ ์ ์์ผ๋ฉฐ,
Member member = Member.builder()
.name("jan")
.age(99)
.address("jan")
.phone("010")
.build();
๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํ ์ ์์ต๋๋ค.
* Member ํด๋์ค์๋ setter ๋ฉ์๋์ public ์์ฑ์๊ฐ ์๊ธฐ ๋๋ฌธ์ Member ๊ฐ์ฒด๋ฅผ ์ป๊ธฐ ์ํด์๋ ์ค์ง Builder ํด๋์ค๋ฅผ ํตํด์๋ง ๊ฐ๋ฅํฉ๋๋ค.
๊ฐ ๋งค๊ฐ๋ณ์๋ฅผ ๋ฃ์ด์ฃผ๋ ๋ฉ์๋ ์ฒด์ด๋ ๊ธฐ๋ฒ์ ์ ์ฉํ๊ณ , ํ์ํ ๋งค๊ฐ๋ณ์๋ฅผ ์ธํ ํ ํ build() ๋ฉ์๋๋ก ๊ฐ์ฒด๋ฅผ ์์ฑํฉ๋๋ค.
์ด๋ ๊ฒ ๋น๋ ํจํด์ ์ฌ์ฉํ๋ฉด ์ด๋ ํ๋์ ์ด๋ค ๊ฐ์ ์ฑ์์ผ ํ๋์ง ๋ช ํํ๊ฒ ์ ์ ์์ผ๋ฉฐ, ๋ฐ์ดํฐ ์ผ๊ด์ฑ, ๊ฐ์ฒด ๋ถ๋ณ์ฑ ๋ฑ์ ๋ง์กฑ์ํค๋ฉฐ ์ฝ๋์ ๊ฐ๋ ์ฑ ๋ํ ํฅ์๋ฉ๋๋ค.
ํ์ง๋ง ๋น๋ ํจํด์ ๊ตฌํ์ ๋ง์ ์ฝ๋์์ด ํ์ํ๊ณ , Builder๋ผ๋ ๊ฐ์ฒด๋ฅผ ํ๋ ๋ ๋ง๋๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ์ฌ์ฉํ๋ ์ฝ๋์ ๋ฐ๋ผ ์ฑ๋ฅ์ด ๋ฎ์์ง ์ ์์ต๋๋ค.
๋ฐ๋ผ์ ํด๋์ค๋ฅผ ์ค๊ณํ ๋ ํ์, ์ ํ ์ธ์๋ค์ด ๋ง์ ๊ฒฝ์ฐ Builder ํจํด์ ์ฌ์ฉํ๋ ๊ฒ์ด ํจ์จ์ ์ ๋๋ค.
+ Lombok ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ์ฉํ ๋น๋ ํจํด
์์ ์์ฑํ ๋น๋ ํจํด์ @Builder ์ด๋ ธํ ์ด์ ํ๋๋ก ๊ตฌํํ ์ ์๋ค.
๊ตฌํํ ๊ฐ์ฒด
@Builder
public class Member {
private String name;
private int age;
private String address;
private String phone;
}
์ปดํ์ผ๋ ํด๋์คํ์ผ
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.example.demo;
public class Member {
private String name;
private int age;
private String address;
private String phone;
Member(String name, int age, String address, String phone) {
this.name = name;
this.age = age;
this.address = address;
this.phone = phone;
}
public static Member.MemberBuilder builder() {
return new Member.MemberBuilder();
}
public static class MemberBuilder {
private String name;
private int age;
private String address;
private String phone;
MemberBuilder() {
}
public Member.MemberBuilder name(String name) {
this.name = name;
return this;
}
public Member.MemberBuilder age(int age) {
this.age = age;
return this;
}
public Member.MemberBuilder address(String address) {
this.address = address;
return this;
}
public Member.MemberBuilder phone(String phone) {
this.phone = phone;
return this;
}
public Member build() {
return new Member(this.name, this.age, this.address, this.phone);
}
public String toString() {
return "Member.MemberBuilder(name=" + this.name + ", age=" + this.age + ", address=" + this.address + ", phone=" + this.phone + ")";
}
}
}
@Builder๋ฅผ Class์์ ์ ์ธํ๋ฉด @AllArgsConstrutor๋ฅผ ๋ถ์ธ ๊ฒ๊ณผ ๊ฐ์ ํจ๊ณผ๋ฅผ ๋ณด๋๋ฐ, Builder Pattern์ ์ฌ์ฉํ ๊ฒฝ์ฐ ํ์ ๋งค๊ฐ ๋ณ์๋ฅผ ์ต์ํ ํด์ฃผ๋ ๊ฒ์ด ์ข๋ค.
@Builder
public class Member {
@NonNull
private final String name;
@NonNull
private final Integer age;
private String address;
private String phone;
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.example.demo;
import lombok.NonNull;
public class Member {
@NonNull
private final String name;
@NonNull
private final Integer age;
private String address;
private String phone;
Member(@NonNull String name, @NonNull Integer age, String address, String phone) {
if (name == null) {
throw new NullPointerException("name is marked non-null but is null");
} else if (age == null) {
throw new NullPointerException("age is marked non-null but is null");
} else {
this.name = name;
this.age = age;
this.address = address;
this.phone = phone;
}
}
public static Member.MemberBuilder builder() {
return new Member.MemberBuilder();
}
public static class MemberBuilder {
private String name;
private Integer age;
private String address;
private String phone;
MemberBuilder() {
}
public Member.MemberBuilder name(@NonNull String name) {
if (name == null) {
throw new NullPointerException("name is marked non-null but is null");
} else {
this.name = name;
return this;
}
}
public Member.MemberBuilder age(@NonNull Integer age) {
if (age == null) {
throw new NullPointerException("age is marked non-null but is null");
} else {
this.age = age;
return this;
}
}
public Member.MemberBuilder address(String address) {
this.address = address;
return this;
}
public Member.MemberBuilder phone(String phone) {
this.phone = phone;
return this;
}
public Member build() {
return new Member(this.name, this.age, this.address, this.phone);
}
public String toString() {
return "Member.MemberBuilder(name=" + this.name + ", age=" + this.age + ", address=" + this.address + ", phone=" + this.phone + ")";
}
}
}
Null ์ฒดํฌ
@NonNull ์ด๋ ธํ ์ด์ ์ ๋ณ์์ ๋ถ์ด๋ฉด ์๋์ผ๋ก null ์ฒดํฌ๋ฅผ ํด์ค๋๋ค. ์ฆ, ํด๋น ๋ณ์๊ฐ null๋ก ๋์ด์จ ๊ฒฝ์ฐ, NullPointerException ์์ธ๋ฅผ ์ผ์ผ์ผ ์ค๋๋ค.