클래스의 생성자, 정적 팩터리 패턴에서는 선택적 매개변수가 많을 때 적용하기 불리한 점이 있다
영양성분을 클래스를 표현한다고 가정하면. 여러 성분 중 해당 식품에 필요한 정보만 선택적으로 입력해야 한다.
class 영양성분{
영양성분(탄수화물,단백질,지방){};
영양성분(탄수화물,단백질,지방,칼로리){};
영양성분(탄수화물,단백질,지방,칼로리,비타민A,비타민B){};
}
이러한 방식으로 모든 상황을 가정하여 생성자를 지정할 수도 있지만(점층적 생성자 패턴)
매개변수가 너무 많아진다면 활용하기 어려워진다.
class 영양성분{
int vitamina;
int vitaminb;
영양성분(탄수화물,단백질,지방){};
public setvitaminA(int num){
vitamina=num;
}
public setvitaminB(int num){
vitaminb=num;
}
}
main(){
영양성분 snack = new 영양성분(1,2,3);
snack.setvitaminA(4);
// 여기서 문제
snack.setvitaminB(5);
}
그런 상황에 대응하기 위해 set메서드를 통해 필드 데이터를 설정해주면(자바빈즈 패턴)
각 필드를 설정하는 도중에 일관성이 깨지게 되고 스레드 안정성을 위해 부가 처리가 필요하다.
이런 문제를 클래스 내부에 정적 빌더 객체를 추가함으로서 해결할 수 있다.
아래는 계층적 빌더 패턴의 예시다.
public abstract class Coffee {
public enum topping {SUGAR,SYRUP,CREAM,CINNAMON};
final Set<topping> toppings;
static public abstract class Builder<T extends Builder<T>>{
EnumSet<topping> toppingEnumSet = EnumSet.noneOf(topping.class);
public T addTopping(topping topping){
toppingEnumSet.add(topping);
return self();
}
abstract Coffee build();
abstract T self();
}
Coffee(Builder<?> build){
toppings=build.toppingEnumSet.clone();
}
}
Coffee는 추상 클래스로 구현될 하위 클래스가 존재한다.
메서드체이닝을 활용할 수 있도록 재귀적 제네릭을 사용한다. (Builder<T extends Builder<T>>)
하위 클래스의 공통 기능이기에 상위 클래스에 구현해둔 addTopping 메서드에서 Builder 클래스를 반환한다면
하위클래스를 생성할 때 메서드체이닝 활용이 불가능하다.
그렇기에 타입 매개변수 제한으로 Builder<T> 클래스를 상한으로 두고
하위클래스를 반환할 수 있도록 구현한다.
생성자에서 참조형 객체인 EnumSet을 .clone() 하고 있다.
Coffee.Builder은 가변형 자료구조인 EnumSet을 사용하고 있기에 그를 = 연산자로 참조하게 되면
static class인 Builder를 사용하는 다른 객체에 의해서 EnumSet이 사용되면 불변성을 유지할 수 없기에
.clone() 메서드를 통해 새로운 EnumSet을 복사 생성하여 관리한다.
.clone() 메서드는 참조형 자료구조의 내부 요소를 얕은복사 하기에 다른 Set 자료구조라면 문제가 생길 수 있지만
enum은 하나의 인스턴스만 존재하기에 얕은 복사로도 불변을 유지할 수 있다.
public class Latte extends Coffee{
public enum milkEnum {NORMAL, VEGAN};
final milkEnum milk;
static public class Builder extends Coffee.Builder<Builder>{
private final milkEnum milk;
public Builder(milkEnum milk){
this.milk=milk;
}
@Override
Latte build(){
return new Latte(this);
}
@Override
Builder self() {
return this;
}
}
Latte(Builder builder){
super(builder);
milk=builder.milk;
}
}
Coffee를 상속한 클래스에서의 Builder는 extends 부분에 상위 클래스의 타입 매개변수를 지정해준다.
추상 메서드였던 self()를 구현하고 있는데, 이도 메서드체이닝을 위한 구현으로
상위 클래스에서 addToppings 후 self()를 반환하고 있는데, 하위클래스의 self() 구현에서 자신을 return 하게 함으로서
공통 상위 메서드에서 구체화 된 클래스를 return 하며 메서드 체이닝이 작동할 수 있도록 한다.
모든 작업을 끝낸 뒤 build() 메서드를 통해 필요한 필드 값을 지정한 인스턴스를 반환할 수 있다.
Comparable (0) | 2025.03.12 |
---|---|
객체 참조 해제, Java 참조 유형 (0) | 2025.03.06 |
enum 매핑 (0) | 2025.02.28 |
자바 클래스 정렬 기준 설정. PriorityQueue, sort. Comporable, Comporator (코테준비) (0) | 2024.10.20 |
Object Class, 불변객체, String (0) | 2024.03.30 |