생성자에 매개변수가 많으면 Builder를 사용하자
28 Mar 2019 | 디자인패턴보통 하나의 메소드가 가지는 매개변수의 개수를 최대 4개 정도까지가 사용하기 무난한 메소드라고 합니다. 그 이상이 될 경우, 해당 메소드를 사용하는 사용자에게 혼란이나 실수를 발생시키기 쉽기 때문에 매개변수를 줄이는 것을 권장합니다.
매개변수를 줄이는 방법으로는 보통 다음 3가지 방법이 있습니다.
- 매개 변수를 쪼갤 수 있도록 메소드를 여러 개로 분할한다.
- 매개 변수를 포함하는 헬퍼 클래스를 하나 생성해서 매소드의 파라메터로 헬퍼 클래스를 사용한다.
- Builder를 사용한다.
여기서는 세 번째 방법인 Builder 사용법에 대한 예시를 알아봅니다.
매개변수가 많은 클래스 예시
public class Robot {
private final String name;
private final String nickName;
private final String controlServerUrl;
private final int width;
private final int height;
private final int x;
private final int y;
private final double orientation;
public Robot(String name, String nickName, String controlServerUrl, int width, int height, int x,
int y, double orientation) {
this.name = name;
this.nickName = nickName;
this.controlServerUrl = controlServerUrl;
this.width = width;
this.height = height;
this.x = x;
this.y = y;
this.orientation = orientation;
}
public Robot(String name, String nickName, String controlServerUrl, int width, int height, int x,
int y) {
this(name, nickName, controlServerUrl, width, height, x, y, 0.0f);
}
public Robot(String name, String nickName, String controlServerUrl, int width, int height) {
this(name, nickName, controlServerUrl, width, height, 0, 0);
}
}
이 경우 해당 클래스를 생성할 때 코드를 작성하거나 읽기가 어려워지며 버그가 발생하기 쉬워집니다.
JavaBeans 패턴 적용
고전적으로 사용하던 JavaBeans 패턴을 이용해서 각 매개변수의 setter를 생성해주는 방법도 있습니다.
package builder;
public class Robot {
private String name;
private String nickName;
private String controlServerUrl;
private int width;
private int height;
private int x;
private int y;
private double orientation;
public void setName(String name) {
this.name = name;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public void setControlServerUrl(String controlServerUrl) {
this.controlServerUrl = controlServerUrl;
}
public void setWidth(int width) {
this.width = width;
}
public void setHeight(int height) {
this.height = height;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setOrientation(double orientation) {
this.orientation = orientation;
}
}
이 경우 인스턴스를 생성하기 위해서는 다음과 같은 코드를 사용합니다.
Robot robot = new Robot();
robot.setName("SuperBot");
robot.setNickName("HiBot");
robot.setHeight(50);
robot.setWidth(30);
robot.setX(10);
robot.setY(20);
robot.setOrientation(250.0f);
하나의 인스턴스 생성을 위해 메소드를 여러 번 호출해야 하기 때문에 객체가 완전히 생성하기 전까지 일관성(Consistency)을 보장받지 못하게 되었습니다.
또한 더 이상 final 키워드를 사용할 수 없기 때문에, 불변성을 갖지 못해 Thread Safety를 보장할 수가 없습니다.
Builder 패턴 적용
Builder 패턴을 적용하면 다음과 같습니다.
public class Robot {
private final String name;
private final int width;
private final int height;
private final int x;
private final int y;
private final double orientation;
public static class Builder {
private final String name; // 필수 매개변수
private int width = 0;
private int height = 0;
private int x = 0;
private int y = 0;
private double orientaiton = 0.0f;
public Builder(String name) {
this.name = name;
}
public Builder width(int value) {
width = value;
return this;
}
public Builder height(int value) {
height = value;
return this;
}
public Builder x(int value) {
x = value;
return this;
}
public Builder y(int value) {
y = value;
return this;
}
public Builder orientation(double value) {
orientaiton = value;
return this;
}
public Robot build() {
return new Robot(this);
}
}
private Robot(Builder builder) {
name = builder.name;
width = builder.width;
height = builder.height;
x = builder.x;
y = builder.y;
orientation = builder.orientaiton;
}
}
사용 예제는 다음과 같습니다.
Robot robot = new Robot.Builder("SuperBot").height(30).width(20).x(100).y(100)
.orientation(250.0f).build();