package kr.co.ezenac.project;
/*
* Builder Pattern
*
* 1. 메서드의 조합으로 결과물을 생성
* 1) 생성에 대한 과정과 각 결과물을 표현하는 방법을 분리하여 동일한 생성 과정에 서로 다른 여러 결과물이 나올 수 있도록 함
* 2) 단계별 생성에 중점을 두는 패턴
* 3) 새로운 결과물이 필요한 경우에도 동일한 과정으로 생성할 수 있음
* 4) 생성 과정과 구현 방법을 분리하여 동일한 생성에서 여러 다른 표현이 나올 수 있음
*
* 2. 생성자를 대체하는 방법
* 1) 객체를 생성할 때 매개 변수가 여러 개인 경우 여러 개의 생성자를 사용하기보다는 인스턴스 생성을 위한 Builder를 제공함으로써
* 이후 매개 변수가 늘어나더라도 유연하게 수정할 수 있는 구조를 제공함
* 2) 스프링 프레임워크에서 사용하고 있음
*
* 3. 객체 생성
* 1) 일반적으로 생성자를 제공
* 2) 때로는 변형이 필요
* - 일반적인 생성자가 제공하는 것 이상이 필요한 경우가 있음
* - 객체 생성을 클래스 로직 밖으로 옮길 필요
* - 객체를 생성하기 전에 점진적으로 객체의 정보를 수집
* - 모든 데이터가 한꺼번에 제공되지 못함
* 3) 결론
* - 제품의 다양한 구현이 가능
* - 제품의 생산 과정을 더 세분화 할 수 있음
* - 클라이언트는 구체적인 사항을 알 필요가 없음
*/
public class Builder {
/*
* 열거형 (Enum Type) : 상수의 집합을 만들거나 특정 객체의 상태를 모아놓음
*/
public enum Directions {
EAST,
WEST,
NORTH,
SOUTH
}
public static void main(String[] args) {
Directions dir = Directions.NORTH;
}
}
import java.util.EnumSet;
import java.util.Objects;
import java.util.Set;
// 여러 가지 피자를 만들기
public abstract class Pizza {
public enum Topping {
HAM,
ONION,
PEPPER,
SAUSAGE,
MUSHROOM
};
final Set<Topping> toppings;
//Builder : Product의 각 요소들을 생성하는데 필요한 추상 메서드가 선언된 클래스나 인터페이스
abstract static class Builder {
EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
public Builder addTopping(Topping topping) {
toppings.add(Objects.requireNonNull(topping));
return self();
}
public Builder sauceInside() {
return self();
}
abstract Pizza build();
protected abstract Builder self();
}
public String toString() {
return toppings.toString();
}
public Pizza(Builder builder) {
toppings = builder.toppings.clone();
}
}
//뉴욕 피자
//ConcreteBuilder : Builder에 선언된 메서드를 구현한 클래스
//NYPizza는 size 값을 필수로 받아 생성
import java.util.Objects;
public class NYPizza extends Pizza {
public enum Size {SMALL, MEDIUM, LARGE};
private final Size size;
public static class Builder extends Pizza.Builder {
private final Size size;
public Builder(Size size) {
//requireNonNull() : 인자 값이 null인지 체크하고 null이 아닐 때 그대로 반환
this.size = Objects.requireNonNull(size);
}
@Override
public Pizza build() {
return new NYPizza(this);
}
@Override
protected Builder self() {
return this;
}
}
//생성자는 최대한 간단하게
public NYPizza(Builder builder) {
super(builder);
this.size = builder.size;
}
}
//칼조네 피자
//ConcreteBuilder : Builder에 선언된 메서드를 구현한 클래스
//Calzone는 sauseInside라는 부울 값을 통해 소스를 넣을지 말지 선택함
public class Calzone extends Pizza {
private final boolean sauceInside;
public static class Builder extends Pizza.Builder {
private boolean sauceInside = false;
public Builder sauceInside() {
sauceInside = true;
return this;
}
@Override
public Calzone build() {
return new Calzone(this);
}
@Override
protected Builder self() {
return this;
}
}
public Calzone(Builder builder) {
super(builder);
this.sauceInside = builder.sauceInside;
}
@Override
public String toString() {
return toppings.toString() + "sauceInside : " + sauceInside;
}
}
public class PizzaTest {
public static void main(String[] args) {
Pizza nyPizza = new NYPizza.Builder(NYPizza.Size.MEDIUM)
.addTopping(Pizza.Topping.SAUSAGE).addTopping(Pizza.Topping.ONION).build();
Pizza calzone = new Calzone.Builder().addTopping(Pizza.Topping.HAM)
.addTopping(Pizza.Topping.PEPPER).sauceInside().build();
System.out.println(nyPizza);
System.out.println(calzone);
}
}