개발/객체지향개발

[디자인패턴] 빌더 패턴

대왕판다 2022. 12. 20. 12:50

1주차 과제로 JSP와 바닐라 자바만으로 게시판을 구현했다.

그 과정에서 아래와 같이 VO, DAO를 하드코딩으로 작성했고

Setter조차 사용하지 않고 무식하게 Post 객체를 생성하는 것에 지적을 받았다.

// PostVO.java
​
@Getter
@Setter
@AllArgsConstructor
public class Post {
    @NonNull
    private Integer post_id;
    @NonNull
    private String title;
    @NonNull
    private String writer;
    @NonNull
    private String password;
    @NonNull
    private String content;
    @NonNull
    private Integer category_id;
  
    private Integer view;
    private Date created_at;
    private Date modified_at;
​
}
​
// PostDAO.java
po = new Post(
  rs.getInt(1),
  rs.getString(2),
  rs.getString(3),
  rs.getString(4),
  rs.getInt(5),
  rs.getString(6),
  rs.getDate(7),
  rs.getDate(8),
  rs.getInt(9)
);

 

위와 같이 작성할 경우

  1. 코드 가독성이 떨어진다.
  2. 생성자 정의가 변경돼 새로운 인자가 추가될 경우, 모든 호출 코드를 변경해주어야 한다!

 

 

따라서

롬복의 builder를 활용해 다시 리팩토링해보라 하셨다.

Project Lombok은 @Builder를 다음과 같에 소개하고 있다.

@Builder 주석은 클래스에 대한 복잡한 작성기 API를 생성합니다.

@Builder를 사용하면 클래스를 다음과 같은 코드로 인스턴스화하는 데 필요한 코드를 자동으로 생성할 수 있습니다.

즉 @Builder 을 통해 빌더 패턴을 위한 보일러플레이트 코드를 일일이 작성하지 않아도 된다.

 

 

그렇다면 빌더 패턴은 무엇인가?

  • 빌더 패턴(Builder pattern) 이란 복합 객체의 생성 과정과 표현 방법을 분리하여 동일한 생성 절차에서 서로 다른 표현 결과를 만들 수 있게 하는 패턴이다. (출처: 위키백과)
  • 생성자에 인자가 많을 때 빌더 패턴을 고려하라고 한다.
  • 빌터패턴을 활용하면 어떤 인자를 넣어줬는지 명확히 알 수 있고,
  • 불필요한 필드(null)은 굳이 선언할 필요가 없다고 한다.
// 빌더 패턴을 활용한 PostVO.java (롬복 없이 보일러플레이트 코드로)
public static class Post {
    private final Integer postId;
    private final String title;
    private final String writer;
    private final String password;
    private final String content;
    private final Integer view;
    private final Date created_at;
    private final Date modified_at;
    private final Integer category_id;
  
    public static class Post(Builder builder){
      this.postId = builder.postId;
      this.title = builder.title;
      this.writer = builder.writer;
      this.password = builder.password;
      this.content = builder.content;
      this.view = builder.view;
      this.created_at = builder.created_at;
      this.modified_at = builder.modified_at;
      this.category_id = builder.category_id;
    }
  
    
    private static class Builder {
      private final Integer postId;
      private final String title;
      private final String writer;
      private final String password;
      private final String content;
      private final Integer category_id;
      private Integer view;
      private Date created_at;
      private Date modified_at;
      
      public Builder(Integer postId, String title, String writer, String password, String                         content, Integer category_id){
        if (postId == null || title == null || writer == null || password == null || content ==             null || category_id == null) {
          throw new IllegalArgumentException("... can not be null");
        }
        this.postId= postId;
        this.title = title;
        // ...
        // 중략
        // ...
        public Builder withView(Integer view){
          this.view = view;
          return this;
        }
        public Builder withCreatedAt(Integer view){
          this.CreatedAt = CreatedAt;
          return this;
        }
        public Builder withModifiedAt(Integer view){
          this.ModifiedAt = ModifiedAt;
          return this;
        }
        
    }
      
// 보일러플레이트 코드로 단순반복하기보다 롬복 @Builder 어노테이션을 사용하면 편리하다.
// 위 코드를 DAO에서 활용할 때
Post post = Post.builder()
                .title(rs.getString(2))
                .writer( rs.getString(3))
                .password(rs.getString(4))
                .content(rs.getString(6))
                .created_at(rs.getDate(6))
                .category_id(rs.getInt(9))
                .build();
​
// 각 필드에 들어가는 인자들도 변수에 담으면 더 잘 보이겠지만..
// 어떤 필드에 어떤 값이 들어가는지 이전보다 명확하게 확인할 수 있다!