2023. 6. 11. 16:16ㆍTip
- 조건 1: 최초 생성 후 수정이 불가능해야 한다.
- 조건 2: Instance 생성 시, name을 제외한 모든 속성에 대한 인자는 optinal이다.
- 조건 3: Instance를 구분할 수 있는 unique key값이 필요하다.
특징
- (조건 1 완료) Person의 모든 멤버 변수가 final형이다. (즉, 최초 생성 간 초기화 이후 수정 불가)
- (조건 2 완료) 생성 인자가 순서로부터 자유롭다. (단, non-null 파라미터는 순서 강제)
- (조건 3 완료) PersonBuilder 내 static 멤버 변수 ID 활용
코드
< 코드 전문 텍스트로 보기 >
public class Main {
public static void main(String[] args) {
// Use Builder Pattern
Person p1 = new Person.PersonBuilder("Tomas")
.age(37)
.height(180)
.weight(78).build();
p1.showInfo();
System.out.println();
Person p2 = new Person.PersonBuilder("2")
.weight(2)
.age(2)
.height(2).build();
p2.showInfo();
System.out.println();
Person p3 = new Person.PersonBuilder("re-assignable")
.name("cannot be null").build();
p3.showInfo();
}
}
public class Person {
private final int id;
private final String name;
private final Integer age;
private final Double height;
private final Double weight;
private Person(int id, String name, Integer age, Double height, Double weight) {
System.out.println(this + " 생성");
this.id = id;
this.name = name;
this.age = age;
this.height = height;
this.weight = weight;
}
public void showInfo() {
System.out.println("id:\t\t" + id);
System.out.println("name:\t" + name);
System.out.println("age:\t" + age);
System.out.println("height:\t" + height);
System.out.println("weight:\t" + weight);
}
protected static class PersonBuilder {
private static int ID = 1;
private String name;
private Integer age;
private Double height, weight;
protected PersonBuilder(String name) {
this.name = name;
}
protected Person build() {
return new Person(ID++, name, age, height, weight);
}
protected PersonBuilder name(String name) {
this.name = name;
return this;
}
protected PersonBuilder age(int age) {
this.age = age;
return this;
}
protected PersonBuilder height(double height) {
this.height = height;
return this;
}
protected PersonBuilder weight(double weight) {
this.weight = weight;
return this;
}
}
}
특징
Person 클래스의 객체는 이 생성자를 통해 최초 1회 생성 및 초기화된다. (멤버 변수 모두 final)
단, private 생성자이므로 외부에서는 직접 호출할 수 없으며,
이후에 소개할 Person.PersonBuilder.build() 를 통해 이 생성자를 호출하게 된다.
** non-null이 보장되지 않는 멤버 변수는 primitive 대신 래퍼 클래스를 사용하여 null값 처리를 손쉽게 했다.
protected static PersonBuilder는 Person과 동일한 패키지에 포함된 Inner class이므로
Person 클래스는 객체 생성 이전 시점부터 정의한 빌더를 사용할 수 있게 된다.
Person 클래스는 이후 PersonBuilder 내부 메소드를 사용해야 하므로,
PersonBuilder 내 모든 메소드의 접근 제어자는 protected가 되어야 한다.
* outer-class Person을 상속하는 class Student 또한 Builder 패턴을 사용해야 한다고 가정해보자. 보다 깔끔한 Package 구조의 측면에서는 아직 생각해보지 않았지만, 막연하게 Builder inner-class의 확장성을 생각한다면 protected를 사용하는 것이 무난할 것 같다. 확장하지 않는다면 접근 제어자를 생략하여 default로 사용하면 더 깔끔해지긴 하겠다.
일반적으로 멤버 변수는 Person과 동일한 형태로 사용하나,
여기서는 조건 3 때문에 ID만 변화를 주었다.
모든 초기화 method x(DataType x)는 this.x=x; return this; 의 형태이다.
마지막 return this; 구문을 통해 이전 PersonBuilder를 이어받아 연속적인 초기화가 가능하다.
물론 build() 호출 이전까지 재할당이 가능하고, 가장 마지막 값이 반영된다.
최종적으로 Person 객체를 만들어 반환하는 메소드 PersonBuilder.build()이다.
이 함수의 호출 이전에는 Person 객체가 생성되지 않으므로, 모든 생성 구문은 반드시 한 줄로만 작성할 수 있다.
즉, Setter가 없는 Person class에서 값을 설정할 수 있는 유일한 순간은 이 객체를 생성할 때가 된다.
'Tip' 카테고리의 다른 글
[MacOS/sdkman] JDK version 관리 (0) | 2023.06.15 |
---|---|
Download Eclipse (0) | 2023.06.15 |
[MySQL] Workbench LOAD DATA LOCAL INFILE ERROR (0) | 2023.05.31 |
[Java] Stream to List (0) | 2023.04.12 |
[해커톤] 웹 개발 팁 정리 (0) | 2023.04.02 |