[Java] Builder 패턴
이전까지 해오던 프로젝트들에서는 DTO를 lombok의 Getter,Setter를 통해 값을 세팅하곤 했었다.
Builder패턴에 대해선 대략적으로 알고있었지만 그 당시에는 왜써야하는지를 이해하지 못하여 넘어갔었는데
최근에 이펙티브 자바를 읽으면서 Builder패턴에 대한 내용이 초반에 나와 학습하게 되었다.
Builder패턴을 왜 쓰게 되었고 왜 써야하는지 일련의 순서로 내용을 정리했다.
아래코드는 일련의 여러 색깔들을 조합으로 새로운 색깔을 만드는 객체가 있다고 가정하여 짠 코드이다.
점층적 생성자를 이용하여 객체를 만드는 방법이고 보다시피 파라미터의 종류/갯수에 따라서 객체를 생성 할 수있다.
//점층적생성자 패턴
public class ColorColection {
private final int red;
private final int blue;
private final int green;
private final int black;
private final int white;
public ColorCollection(int red){
this(red);
}
public CollorCollection(int red,int blue){
this(red,blue);
}
public CollorCollection(int red,int blue,int green){
this(red,blue,green);
}
public CollorCollection(int red,int blue,int green,int black){
this(red,blue,green,black);
}
public CollorCollection(int red,int blue,int green,int white){
this(red,blue,green,black,white);
}
}
//객체 생성시 사용 코드
//red
ColorCollection = new ColorCollection(1);
//red,blue
ColorCollection = new ColorCollection(1,3);
//red,blue,green,black
ColorCollection = new ColorCollection(1,5,2,1);
다만 이 방법의 파라미터 갯수와 순서가 명시되어 있지 않아 가독성이 매우 떨어지고 객체 생성시 실수가 나올수 있어 좋지 않은방법이다.
그래서 이를 보완하고자 나온 방법이 자바빈즈(JavaBeans)패턴이다
//자바빈즈 패턴
public class ColorColection {
private int red;
private int blue;
private int green;
private int black;
private int white;
public ColorCollection(){}
public void setRed(int val) {red=val;}
public void setBlue(int val){ blue=val;}
public void setGreen(int val){ green=val;}
public void setBlack(int val){ black=val;}
public void setWhite(int val){ white=val;}
}
//객체생성시 사용코드
ColorCollection colorCollection= new ColorCollection();
colorCollection.setRed(1);
colorCollection.setGreen(3);
colorCoolection.setWhite(10);
점층적생성자를 통한 객체생성보다 가독성이 훨씬 개선되었다. 그리고 이전에 dto세팅을 할때 내가 자주쓰던 패턴이다.
하지만! 자바빈즈 패턴도 단점이 존재하는데..
객체하나를 만들기 위해 여러 메소드를 호출해야하고, 객체가 완전히 생성되기전에 일관성이 무너진 상태에 놓이게 된다는것이다.
점층적생성자를 이용할땐 파라미터들이 유효한지를 생성자에서 확인하면 일관성을 유지할수 있었지만 자바빈즈패턴에서는 그러지 못하게 된것이다.
그리고 일관성이 깨진 객체가 만들어지면 버그나 오류발생시 디버깅이 만만치 않다는 단점도 생기게 된다.
(@Setter를 지양해야 하는 이유..!)
그리고 이러한 단점들은 보완하고 점층적생성자의 안정성과 자바빈즈의 가독성의 장점만을 살린것이 빌더 패턴이다.
빌더패턴의 원리는 다음과 같다
1) 필요한 객체를 직접 만드는 대신, 생성자(혹 정적팩토리)를 호출해 빌더 객체를 얻는다.
2) 빌더 객체가 제공하는 일종의 Setter메소드로 원하는 파라미터들을 설정한다.
3) 파라미터가 없는 build메소드를 호출해 객체를 얻는다.
public class ColorColection {
private final int red;
private final int blue;
private int green;
private int black;
private int white;
//생성자. 파라미터는 내부 Builder객체에서 생성된 Builder를 받는다.
public ColorColection(Builder builder){
red=builder.red;
blue=builder.blue;
green=builder.green;
black=builder.black;
white=builder.white;
}
public static class Builder{
private int red;
private int blue;
private int green;
private int black;
private int white;
//Setter역할하는 메소드들. 필드값과 동일한이름으로 지어준다
//리턴값으로 자기 자신객체 this를 호출함으로써 메소드 체이닝 방식으로 연속적으로 메소드를 호출할수있게 된다.
public Builder red(int val){
red=val;
return this;
}
public Builder blue(int val){
blue=val;
return this;
}
public Builder green(int val){
green=val;
return this;
}
public Builder black(int val){
black=val;
return this;
}
public Builder white(int val){
white=val;
return this;
}
//Builder패턴사요이 마지막에 build()메소드를 호출함으로써
//만들고자하는객체의 파라미터에 builder객체를 담아서 새로 생성한다.
//상단의 ColorCollection생성자 코드를 참고하자
public ColorColection build(){
return new ColorColection(this);
}
}
}
위의 코드를 바탕으로 실제로 Builder패턴을 외부에선 아래와 같이 쓰게된다.
public class ColorService {
public ColorCollection collect() {
ColorCollection collection = ColorCollection.builder()
.red(1)
.blue(2)
.green(5)
.black(2)
.white(0)
.build();
return collection;
}
}
위와같은 빌더패턴으로 객체 생성시 장점은 다음과 같다
1. 객체생성에 필요한 필드값들을 가독성있게 확인할 수 있다. (점층적생성자의 경우 필드의 값만 명시되기에 1차원적으로 봤을때 어떤필드인지 알 수 없다.)
2. 객체의 일관성을 보장해준다. ( JavaBeans패턴의 경우 필드별로 set메소드를 써줘야 함으로 실수로 특정 필드를 빠뜨리거나 할 경우 일관성 보장 X)