Effective Java - Item 2. 생성자에 매개변수가 많다면 빌더를 고려하라

생성자에 매개변수가 많다면 빌더 패턴을 이용하는 것이 더 편하다.

생성자를 사용하여 초기화

constructor 으로 많이 파라미터를 받아야 한다면, 파라미터의 조합에 의해 많은 constructor 을 작성하지 않으면 안될 수도 있다. 많은 생성자를 유지 관리하는 것이 어려울 뿐만 아니라, 생성자 호출자의 코드를 보면 각 매개 변수가 무엇을 나타내는지 알기 어렵다는 문제가 발생한다.

// 페라미터의 의미가 알 수가 없다 StreamInfo info1 = new StreamInfo(200, 150, true, false); StreamInfo info2 = new StreamInfo(200, 150, false);

JavaBeans Pattern 사용하여 초기화

복수 필드의 초기화 방법으로서는 constructor 에서는 파라미터를 받지 않고, 인스턴스를 생성한 후에 복수의 setter 를 호출하는 것으로 필드를 설정하는 방법도 생각할 수 있다.

// JavaBeans 패턴 StreamInfo info = new StreamInfo(); info.setWidth(200); info.setHeight(150); info.setCaption(true); info.setProgressive(false);

이 방법으로 필드의 값을 설정하면 어느 값이 어떤 필드에 대해 설정되어 있는지 명확하게 된다. 그러나 setter 를 제공한다는 것은 그 객체를 불변(Immutable)으로 할 수 없는 것을 나타내고 있어, 멀티스레드 설계에 있어서 안전하게 취급할 수 있는 클래스를 만드는 것이 어렵게 된다.

Builder Pattern을 사용하여 초기화

그래서 빌더 패턴이다. Builder 패턴에서는 인스턴스 생성용의 파라미터를 보관 유지하는 Builder 객체를 제공하고, 반드시 이 객체 사용하여 생성하고자 하는 객체를 생성하도록 한다. Builder 클래스에는 이름 붙은 setter 메소드를 제공하기 위해, 각 파라미터를 설정하는 코드를 알기 쉬워진다.

// Builder 패턴으로 생성할 수 있도록 한 StreamInfo 클래스 public class StreamInfo { private final int width; private final int height; private final boolean hasCaption; private final boolean isProgressive; /** To create an instance of StreamInfo, use Builder#build() instead. */ private StreamInfo(Builder builder) { width = builder.width; height = builder.height; hasCaption = builder.hasCaption; isProgressive = builder.isProgressive; } public int getWidth() { return width; } public int getHeight() { return height; } public boolean hasCaption() { return hasCaption; } public boolean isProgressive() { return isProgressive; } public static class Builder { private int width; private int height; private boolean hasCaption; private boolean isProgressive; public Builder setWidth(int width) { this.width = width; return this; } public Builder setHeight(int height) { this.height = height; return this; } public Builder setCaption(boolean hasCaption) { this.hasCaption = hasCaption; return this; } public Builder setProgressive(boolean isProgressive) { this.isProgressive = isProgressive; return this; } public StreamInfo build() { // If necessary fields have not been set yet, // IllegalStateException can be thrown here. return new StreamInfo(this); } } }

Builder 객체를 사용한 인스턴스 생성 예는 다음과 같다.

StreamInfo info = new StreamInfo.Builder().setWidth(200).setHeight(150) .setCaption(true).setProgressive(false).build();

생성된 StreamInfo객체에는 setter가 없으므로, Immutable이며 스레드로부터 안전하다. Builder 객체는 재활용이 가능하기에, 필드의 일부만 다른 객체를 생성하려는 경우에도 활용할 수 있다.

빌더 패턴의 단점은 객체 생성 시 각 필드를 복사하기 위해 약간의 성능 저하를 초래한다는 것이다. 다만, 멀티스레드 프로그래밍에 있어서, Immutable인 객체는 배타적 제어 없이 액세스 할 수 있다고 하는 큰 이점을 가지고 있다. 그 결과적으로 속도적으로도 유리하게 되는 경우가 많다.




최종 수정 : 2023-05-06