간펴니
간편 자바프로그래밍
간펴니
전체 방문자
오늘
어제
  • 전체보기 (185)
    • 알고리즘 (2)
    • JAVA (69)
      • 이펙티브 자바 (47)
      • JAVA 병렬프로그래밍 (5)
      • 자바 (17)
    • SPRING (60)
      • Spring (12)
      • IceWater Community (37)
      • Homme Shop (10)
      • 토비의 스프링 (1)
    • SPRING BOOT (4)
      • WhiteRecord (7)
    • 오류 (9)
    • DB (10)
      • ORACLE (5)
      • MYSQL (1)
      • MYBATIS (4)
      • JPA (0)
      • 대용량 데이터 베이스 (0)
      • SQL (0)
    • FRONT (8)
      • JSP (2)
      • JavaScript (2)
      • Jquery (3)
      • Thymeleaf (1)
    • AWS (6)
    • JNI (10)
    • 회고 (0)
    • MQ (0)
    • Radis (0)
    • Git (0)
    • Docker (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

  • 블로그 컨셉 변경

인기 글

태그

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
간펴니

간편 자바프로그래밍

JAVA/이펙티브 자바

2. 생성자에 매개변수가 많다면 빌더를 고려하라

2023. 6. 8. 21:31
728x90

 

정적 팩터리 메서드와 생성자에는 같은 제약이 있다.

 

매개변수의 변동이 많을 때 적절히 대응하기 어려워 지는 것이다.

 

프로그래머들은 이럴 때 점층적 생성자 패턴을 즐겨 사용했다.

 

점층적 생성자 패턴이란 쉽게 말해 여러개의 생성자를 만드는 것이다.

 

매개변수가 다른 생성자들을 만들어 각각의 상황에 대응하지만 한계가 명확히 보인다.

 

코드가 복잡해지는 것 뿐만 아니라 매개변수가 추가되거나 변경될 때 마다 생성자를 변경해줘야 한다.

 

 그 다음은 getter/setter를 사용하는 자바 빈즈 패턴이 있다.

 

new로 객체를 생성하고, setter를 이용해 값을 넣어줄 수 있다.

 

하지만 객체가 완전히 생성되기 전까지는 일관성이 무너진 상태에 놓이게 된다.

 

값이 유효한지 알 수 없고, 값을 어디까지 설정해줘야 하는지 알 수 없다.

 

 

빌더 패턴은 점층적 생성자 패턴의 안전성과 자바 빈즈 패턴의 가독성을 가지고 있다.

 

빌더는 보통 생성할 클래스 안에 정적 클래스로 만들어 둔다.

 

@Getter
@ToString
public class FileDescription {

  public enum WriterTypes {a, b, c, d, e}

  private final String path;
  private final String directory;
  private final String filename;

  @Builder(toBuilder = true)
  public FileDescription(String directory, String filename) {

    this.directory = directory;
    this.filename = filename;
    this.path = directory+filename ;

  }

  public static FileDescription newInstance(WriterTypes writerTypes, LogDescription description) {


    FileDescriptionBuilder builder = FileDescription.builder();
    builder.directory("/c")

    );

    switch (writerTypes) {

      case a -> builder.filename("a");

      case b -> builder.filename("b");

      case c -> builder.filename("c");

      case d -> builder.filename("d");
	
    default -> builder.filename("e");
    }

    return builder.build();

  }

 

프로젝트 중 사용한 builder와 정적 팩터리 메서드이다.

 

아이템 1에서 확인했던 정적 팩터리 메서드를 builder와 같이 사용하면 이런 방식으로 사용 할 수 있게 된다.

 

 

    FileDescription file = FileDescription.builder().directory("a").filename("a").build();

 

위는 빌더 패턴으로 FileDescription 객체를 생성하는 방법이다. 

 

이 빌더의 클라이언트 코드는 불변이고, 쓰기 쉽고, 읽기 쉽다. 이것이 빌더를 사용하는 이유다.

 

@toBulider 어노테이션을 true로 설정해주면 기존 값에서 값을 변경한 새로운 객체를 만들 수 있다. ( 같은 객체 x )

 

 

빌더 패턴은 계층적으로 설계된 클래스와 함께 사용하기에 좋다.

 

추상클래스에 빌더를 만들고, 각 하위 클래스의 빌더가 정의한 빌더는 해당하는 하위 클래스를 반환하도록 선언한다.

 

public abstract class Pizza { 

public enum Topping {HAM, MUSHROOM, ONION, PEPPER, SAUSAGE}

    final Set<Topping> toppings;

    abstract static class Builder<T extends Builder<T>> {
        EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);

        public T addTopping(Topping topping) {
            toppings.add(Objects.requireNonNull(topping));
            return self();
        }

        abstract Pizza build();

        protected abstract T self();
    }

    Pizza(Builder<?> builder){
        toppings = builder.toppings.clone();
    }

}

 

 

 

public class NewYorkPizza extends Pizza {

private final Size size;

    public enum Size {
        SMALL,
        MEDIUM,
        LARGE
    }

    public static class Builder extends Pizza.Builder<Builder> {
        private final Size size;

        public Builder(final Size size) {
            this.size = Objects.requireNonNull(size);
        }

        @Override
        public NewYorkPizza build() {
            return new NewYorkPizza(this);
        }

        @Override
        protected Builder self() {
            return this;
        }
    }

    NewYorkPizza(final Builder builder) {
        super(builder);
        size = builder.size;
    }

}

이렇게 추상클래스에 enum과 빌더를 적용 시킬 수 있다.

 

! 하지만 빌더 패턴은 객체를 만들기 전, 빌더를 생성해야 한다.

 

빌더 생성 비용이 크지는 않지만 성능에 민감한 상황에서는 문제가 될 수도 있다.

 


 

기존 프로젝트에도 생성자보다는 빌더를 주로 활용했지만 왜? 어떠한 장점 때문에? 라는 것은 정확하게 알고 있지 않았다.

 

코드가 간결해지고 불변조건이 생긴다 정도만 알고 있었지만, 자세히 알게되니 앞으로 더욱 잘 활용 할 수 있을 것 같다.

 

역시 알고 쓰는 것과 모르고 쓰는 건 천지차이 인 것 같다..

 

728x90
저작자표시 (새창열림)

'JAVA > 이펙티브 자바' 카테고리의 다른 글

5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라  (0) 2023.06.11
4. 인스턴스화를 막으려면 private 생성자를 사용하라  (0) 2023.06.10
3. private 생성자나 열거타입으로 싱글턴을 보증하라  (0) 2023.06.10
1. 생성자 대신 정적 팩터리 메서드를 고려하라  (0) 2023.06.07
HashMap이란?  (0) 2021.08.26
    'JAVA/이펙티브 자바' 카테고리의 다른 글
    • 4. 인스턴스화를 막으려면 private 생성자를 사용하라
    • 3. private 생성자나 열거타입으로 싱글턴을 보증하라
    • 1. 생성자 대신 정적 팩터리 메서드를 고려하라
    • HashMap이란?
    간펴니
    간펴니
    개발공부 기록하는 곳

    티스토리툴바