on
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!!'}
이렇게 객체를 생성했을 때 마다 느낀 것은
너무 불편하다…
뭐가 불편하냐면,
- 내가 빠짐없이
set
method를 써줬는지 일일이 확인을 해줘야 한다. 가끔 빼먹었을 때도 많았다. - 코드를 적기도 힘들다.
- 그렇다고 생성자에 하나씩 넣어서 하려고 하니, 어느 위치에 어떤 값을 넣어야 하는지 헷갈리고 내가 넣기 싫은 값은
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를 호출하기만 하면 되기 때문이다.