Builder pattern

Design Pattern 공부

디자인 패턴 공부를 모아놓은 tag 입니다.


Builder pattern

builder pattern : OOP에서 다양한 객체 생성 문제에 대한 유연한 해결책을 제공하기 위해 설계된 design pattern이다. - 출처

객체의 생성에 관한 design pattern이라는 것은 알겠는데 확 와닿지가 않는다. 그러나 다음과 같은 상황을 겪으신 분들이라면 이 패턴에 무한한 감사를 하게 될 수 있다.


기존에 객체를 생성하는 방법

기존에 Spring-boot project를 했을 때 나는 객체 생성을 다음과 같이 했다. (현재 객체를 생성하는 것이기에 Entity라는 표현은 쓰지 않았습니다)

/**
 * builder pattern 에 대해 알아보기 위한 test 용 객체 입니다.
 */
@Getter @Setter
public class Posting {
	private String title;
	private String subtitle;
	private String content;
	private String category;
	private String author;

	public Posting() {}

	public Posting(String title, String subtitle, String content, String category, String author) {
		this.title = title;
		this.subtitle = subtitle;
		this.content = content;
		this.category = category;
		this.author = author;
	}
}

일단 테스트용 객체를 하나 만들었다. 그리고 아래와 같이 생성하였다.

public class NormalApp {
	public static void main(String[] args) {
		/**
		 * 이렇게 하면 안 좋은 점
		 * 1. 장황해진다. 딱 봐도 코드가 field 의 개수에 따라서 더 늘어나게 된다.
		 * 2. 추가하고 싶은 부분만 추가하고 싶어도 method 가 많이 만들어져야 하거나,
		 * 		매번 set method 를 필요한 것만 쓸려면 실수를 하게 될 수 도 있다. → 불완전하게 만들어질 수 있다.
		 * 3. 그렇다고 생성자를 사용한다면...?
		 * 		1) 2번과 같은 문제 발생. 생성자가 많아져야하는 경우가 생길 수 있다.
		 * 		2) 1번과 같은 문제 발생. 생성자에 어떤 파라미터가 어디에 들어가야 하는지 알기 힘들다. 장황해진. 따라서 가독성이 떨어진다.
		 */
		Posting posting = new Posting();
		posting.setTitle("this is title");
		posting.setSubtitle("this is subtitle");
		posting.setContent("안녕하세요. 이것은 본문에 관한 field 라고 생각하시면 됩니다.");
		posting.setCategory("자유게시판");
		posting.setAuthor("this is me!!");

		System.out.println("posting : " + posting);
	}
}
posting : Posting{title='this is title', subtitle='this is subtitle', content='안녕하세요. 이것은 본문에 관한 field 라고 생각하시면 됩니다.', category='자유게시판', author='this is me!!'}

이렇게 객체를 생성했을 때 마다 느낀 것은

너무 불편하다…

뭐가 불편하냐면,

  1. 내가 빠짐없이 set method를 써줬는지 일일이 확인을 해줘야 한다. 가끔 빼먹었을 때도 많았다.
  2. 코드를 적기도 힘들다.
  3. 그렇다고 생성자에 하나씩 넣어서 하려고 하니, 어느 위치에 어떤 값을 넣어야 하는지 헷갈리고 내가 넣기 싫은 값은 null로 넣어주거나 다른 생성자를 만들어주는 불편함이 생긴다.

일단 이정도가 너무 불편했다. 물론 저렇게 해도 우리가 원하는 객체를 만들 수 있다. 하지만 개발자라면 불편함을 감수하는 것이 아닌, 극복하는 것!!! Builder Pattern은 명확하게 우리가 추가하고 싶은 field만 추가할 수 있게 해준다. 다음 코드를 보도록 하자.


Default Builder Pattern

public interface PostingBuilder {
	PostingBuilder title(String title);

	PostingBuilder subtitle(String subtitle);

	PostingBuilder content(String content);

	PostingBuilder category(String category);

	PostingBuilder author(String author);

	Posting getPosting();
}

먼저 interface를 정의하여 우리가 원하는 구현체를 바꿔가며 쓸 수 있게 한다. 지금 getPosting() method 말고는 모두 반환 type이 PostingBuilder임을 볼 수 있다. 이렇게 반환 타입을 설정한 이유는 ‘연속해서 본인의 method를 호출하기 위함’이다. 말로하면 이해가 잘 안갈 수 있는데 좀 있다가 알게 될 것이다.


import javastudy.designpattern.builder.Posting;

public class DefaultPostingBuilder implements PostingBuilder {
	private String title;
	private String subtitle;
	private String content;
	private String category;
	private String author;

	@Override
	public PostingBuilder title(String title) {
		this.title = title;
		return this;
	}

	@Override
	public PostingBuilder subtitle(String subtitle) {
		this.subtitle = subtitle;
		return this;
	}

	@Override
	public PostingBuilder content(String content) {
		this.content = content;
		return this;
	}

	@Override
	public PostingBuilder category(String category) {
		this.category = category;
		return this;
	}

	@Override
	public PostingBuilder author(String author) {
		this.author = author;
		return this;
	}

	@Override
	public Posting getPosting() {
		return new Posting(title, subtitle, content, category, author);
	}
}

기본적인 builder pattern 구현체이다. field들을 선언하여 해당 field에 값을 채워넣는 방식으로, getPosting() method를 호출하게 되면 해당 field의 값들로 Posting객체를 만들어주게 된다. 테스트를 한번 해보자.

import javastudy.designpattern.builder.Posting;

public class FirstBuilderApp {
	public static void main(String[] args) {
		PostingBuilder postingBuilder = new DefaultPostingBuilder();

		/**
		 * NormalApp 에서 테스트 했을 때 보다 코드가 더 보기 편해졌다.
		 * 만약에 이러한 형식으로 입력이 많이 들어온다면, 이 형식 set 을 Director 에 넣어놓고 사용하면 된다.
		 */
		Posting posting = postingBuilder.title("이것은 제목")
				.subtitle("이것은 부제목")
				.content("이것은 내용")
				.category("카테고리 종류")
				.author("저자").getPosting();

		System.out.println("posting = " + posting);
	}
}
posting = Posting{title='이것은 제목', subtitle='이것은 부제목', content='이것은 내용', category='카테고리 종류', author='저자'}

한 눈에 봐도, 처음보다 명확하게 느껴진다. 해당 값이 어느 field에 들어가는지 알기 쉽고, 내가 원하는 field에만 값을 넣기에도 편하다. 그냥 해당 method를 호출하기만 하면 되기 때문이다.


Reference

GoF 디자인 패턴 - 인프런 백기선 강사님

builder pattern