본문 바로가기

Java/Design patterns

객체 생성을 위한 패턴 - 빌더(Builder), 점층적 생성자(Telescoping constructor), 자바빈(JavaBeans) 패턴

점층적 생성자 패턴(Telescoping constructor pattern)

점층적 생성자 패턴(Telescoping constructor pattern)은 생정자(Constructor)의 호출 인자(Parameter)에 대해서 점층적으로 확장하며 오버로딩(Overloading) 정의하는 패턴입니다.

이 패턴은 다음과 같은 방식으로 구현됩니다.

  • 필수 인자를 사용하는 생성자를 정의합니다.
  • 필수 인자와 1개의 선택적인 인자를 사용하는 생성자를 정의합니다.
  • 필수 인자와 2개의 선택적인 인자를 사용하는 생성자를 정의합니다.
  • ...
  • 모든 인자를 사용하는 생성자를 정의합니다.
/**
 * 이 클래스는 사람에 대한 속성을 정의합니다.
 * 점층적 생성자 패턴에 따라서 생성자를 정의합니다.
 */
public class People
{
	public String name;
	public String gender;
	public int age;
	
	/**
	 * 필수 인자를 사용하는 생성자를 정의합니다.
	 * 부족한 인자는 다음 오버로딩 생성자를 호출하면서 채워집니다.
	 */
	public People(String name)
	{
		this(name, "남성");
	}
	
	/**
	 * 필수 인자와 1개의 선택적인 인자를 사용하는 생성자를 정의합니다.
	 * 부족한 인자는 다음 오버로딩 생성자를 호출하면서 채워집니다.
	 */
	public People(String name, String gender)
	{
		this(name, gender, 1);
	}
	
	/**
	 * 모든 인자를 사용하는 생성자를 정의합니다.
	 */
	public People(String name, String gender, int age)
	{
		this.name = name;
		this.gender = gender;
		this.age = age;
	}
}

점층적 생성자 패턴은 객체(Instance)를 생성하기 위한 인자를 선택적으로 생략 할 수 있다는 장점이 있습니다.

예를 들어, 남자 아이에 대한 출생 신고를 일괄 처리하는 함수의 경우 아래와 같이 작성 될 수 있습니다.

public People[] born(String ... names)
{
	People[] people = new People[names.length];
	int i = 0;
	for (String name : names)
		people[i++] = new People(name);
	return people;
}
더보기

남자 아이가 출생하였을 때, gender="남성"이면서 age=1을 자동 기입합니다.

반면 생성자를 사용하기 위한 인자가 많아질수록 가독성이 떨어지는 단점이 있습니다.

다음 예시처럼 인자가 많아지면 각 인자가 어떤 의미로 사용되는지 한 눈에 파악되지 않습니다.

가독성이 떨어지면 생산성이 낮아지고 휴먼 에러의 가능성이 높아집니다.

new People("홍길동", "남성", 28, "031-9999-9999", "010-1234-5678", 43);
더보기

성능 좋은 컴파일러의 경우 인자의 변수명 등을 알려주기는 하지만, 이것만으로는 충분하지 않습니다.

자바빈 패턴(JavaBeans pattern)

점층적 생성자 패턴의 문제점을 보완하기 위한 방법으로 자바빈 패턴(JavaBeans pattern)을 사용 할 수 있습니다.

자바빈 패턴은 JSP의  자바빈(Java Bean)처럼 Setter 메소드를 구현하여 필드 값을 채워나가는 방식입니다.

Setter 메소드 명이 명확하게 네이밍되고 있다면, 각 인자의 의미를 파악하기 훨씬 수월해집니다.

또한 생성자를 인자 개수만큼 오버로딩하는 수고로움 역시 덜 수 있습니다.

People people = new People();
people.setName("홍길동");
people.setGender("남성");
people.setAge(28);

하지만 자바빈 패턴의 경우 객체 생성에 대한 일관성이 없다는 문제가 있습니다.

일관성이 없기 때문에 어떤 경우에는 Setter 메소드가 누락될 위험이 있습니다.

  • 다른 코드에서 객체 생성을 하는 과정에서 누락
  • 새로운 Setter 메소드가 추가될 때 기존 코드에서의 누락

그리고 Setter 메소드로 인해 불변(Immutable) 클래스를 만들 수 없다는 단점 또한 가지고 있습니다.

빌더 패턴(Builder pattern)

빌더 패턴에서는 점층적 생성자 패턴과 자바빈 패턴에서의 이슈를 해결하기 위한 목적으로 사용됩니다.

  • 인자가 사용되는 의미가 불분명하여 가독성이 떨어지는 문제
  • 객체 생성에 대한 일관성 문제
  • 불변(Immutable) 클래스 생성이 가능한 형태 유지

빌더 패턴에서는 흔히 Builder라고 부르는 클래스를 정의합니다.

Builder 클래스에서는 패턴이 사용되려는 클래스에 대한 객체 생성을 직접적으로 관리하게 됩니다.

이 때 객체의 필수 인자는 Builder의 생성자에 의해서 전달되고, 선택적 인자는 Builder의 메소드 체이닝 패턴(Method chaining pattern)으로 구성하는 것이 일반적입니다.

/**
 * 이 클래스는 사람에 대한 속성을 정의합니다.
 * 빌더 패턴에 의해서 Builder 클래스를 추가 정의합니다.
 */
public class People
{
	private String name;
	private String gender;
	private int age;
	
	public People(People.Builder builder)
	{
		this.name = builder.name;
		this.gender = builder.gender;
		this.age = builder.age;
	}
	
	public static class Builder
	{
		// 필수 인자는 빌더에 의해서 파라미터를 요구합니다.
		private String name;
		
		// 선택적 인자는 default 값을 입력합니다.
		private String gender = "남성";
		private int age = 1;
		
		public Builder(String name)
		{
			this.name = name;
		}
		
		public Builder gender(String gender)
		{
			this.gender = gender;
			return this;
		}
		
		public Builder age(int age)
		{
			this.age = age;
			return this;
		}
	}
}

빌더 패턴을 사용하면 다음과 같이 객체를 생성 할 수 있습니다.

객체가 Builder에 의해서 한 번에 생성되므로 일관성을 유지하기 좋고, 인자가 사용되는 의미가 명확하며 생성하려는 클래스가 Setter 메소드를 갖고있지 않으므로 불변 클래스로써 사용에도 적합합니다.

People people = new People(new People.Builder("홍길동")
	                                 .gender("남성")
	                                 .age(28));