[Java][Constructor][Pattern] Builder Pattern

2022. 2. 15. 18:38JAVA/Overview

학습목표
1. 객체를 생성할때 문제에 대해서 학습 (인스턴스화)
2. 인스턴스화의 문제를 해결하기 위한 여러 패턴에 대해서 학습
3. Builder Pattern에 대해서 학습

 

1. 인스턴스화(Instantiate)

아래와 같이 Student 클래스 타입의 객체를 생성한다고 가정합니다. 

public class Student {
	private int stdno;	// 학번
	private String name;// 이름
	private String dept;// 학과
	private int age;	// 나이
	
	public Student(int stdno, String name, String dept, int age) {
		this.stdno = stdno;
		this.name = name;
		this.dept = dept;
		this.age = age;
	}

	@Override
	public String toString() {
		return "Student [stdno=" + stdno + ", name=" + name + ", dept=" + dept + ", age=" + age + "]";
	}
	
}
public class Driver {

	public static void main(String[] args) {
		// 한 생성자에 모든 필드 멤버를 정의하면 객체 생성시 어떠한 인자값이 어떤 필드멤버를 나타내는지 어려움
		// 이러한 상황은 가독성이 매우 떨어짐
		Student std1 = new Student(1, "홍길동", null, 20);
		
		System.out.println(std1);
	}
}

위와 같이 생성자에 모든 필드 멤버를 정의하면 객체 생성시 어떠한 인자값이 어떤 필드 멤버를 나타내는지 알 수가 없습니다. 위와 같은 상황은 가독성이 매우 떨어집니다.

 

2. 점층적 생성자 패턴(Telescoping Constructor Pattern)

생성자를 정의시 필드 멤버를 점층적으로 정의하여 모든 경우에 대해서 객체를 생성합니다.

public class Student {
	private int stdno;	// 학번
	private String name;// 이름
	private String dept;// 학과
	private int age;	// 나이
	
	public Student() {
		this(0,"","",0);
	}
	public Student(int stdno) {
		this(stdno,"","",0);
	}
	public Student(int stdno, String name) {
		this(stdno,name,"",0);
	}
	public Student(int stdno, String name, String dept) {
		this(stdno,name,dept,0);
	}
	public Student(int stdno, String name, String dept, int age) {
		this.stdno = stdno;
		this.name = name;
		this.dept = dept;
		this.age = age;
	}

	@Override
	public String toString() {
		return "Student [stdno=" + stdno + ", name=" + name + ", dept=" + dept + ", age=" + age + "]";
	}
	
}
public class Driver {

	public static void main(String[] args) {
		
		// 인스턴스화의 가독성 문제를 해결하기 위해서 생성자 오버로딩을 통해서
		// 생성자를 오버로딩함
		// 객체 생성시에는 가독성이 좋아질 순 있어도 Student 클래스는 그렇지 않음
		Student std1 = new Student(1, "홍길동");
		
		System.out.println(std1);

	}

}

위와 같이 설계시 객체를 생성할때 가독성은 좋아져도 클래스 설계 부문에서 가독성이 좋아지지 않는 문제를 가지고 있습니다.

 

3. 자바 빈 패턴(JavaBeens Pattern)

자바 빈 패턴은 객체를 생성한 다음 setter 메서드를 통해서 값을 설정하는 패턴입니다.

public class Student {
	private int stdno;	// 학번
	private String name;// 이름
	private String dept;// 학과
	private int age;	// 나이

	public Student() {
		
	}

	public Student(int stdno, String name, String dept, int age) {
		this.stdno = stdno;
		this.name = name;
		this.dept = dept;
		this.age = age;
	}
	
	public void setStdno(int stdno) {
		this.stdno = stdno;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setDept(String dept) {
		this.dept = dept;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Student [stdno=" + stdno + ", name=" + name + ", dept=" + dept + ", age=" + age + "]";
	}
	
}
public class Driver {

	public static void main(String[] args) {
		
		// 자바빈 패턴
		// 객체를 생성하고 나서 setter 메서드를 통해서 설정함
		// 생성자의 영향을 받지 않기 때문에 생성자를 많이 구현할 필요가 없음
		// 문제는 한번의 생성자 호출로 끝낼수 없기 때문에 일관성이 깨질 수 있음
		// 그리고 변하지 않는 immutable한 객체를 생성할 수 없음
		Student std1 = new Student();
		
		std1.setStdno(1);
		std1.setName("홍길동");
		std1.setDept("컴퓨터공학과");
		std1.setAge(20);
		
		System.out.println(std1);

	}
}

하지만 자바 빈 패턴으로 생성자를 정의 시 한번의 생성자 호출로 끝낼 수 없기 때문에 일관성이 깨질 수 있고 변하지 않는 immutable한 객체를 생성할 수 없는 문제를 가지고 있습니다.

 

4. 빌더 패턴(Builder Pattern)

빌더 패턴은 점층적 생성자 패턴과 자바 빈 패턴의 장점을 결합한 패턴입니다.

public class Student {
	private int stdno;	// 학번
	private String name;// 이름
	private String dept;// 학과
	private int age;	// 나이

	
	public Student(Builder builder) {
		this.stdno = builder.stdno;
		this.name = builder.name;
		this.dept = builder.dept;
		this.age = builder.age;
	}

	static class Builder{
		private int stdno;	// 학번
		private String name;// 이름
		private String dept;// 학과
		private int age;	// 나이
		
		public Builder(int stdno) {
			this.stdno = stdno;
		}
		
		public Builder name(String name) {
			this.name = name;
			return this;
		}
		
		public Builder dept(String dept) {
			this.dept = dept;
			return this;
		}
		
		public Builder age(int age) {
			this.age = age;
			return this;
		}
		
		public Student build() {
			return new Student(this);
		}
	}

	@Override
	public String toString() {
		return "Student [stdno=" + stdno + ", name=" + name + ", dept=" + dept + ", age=" + age + "]";
	}
	
}
public class Driver {

	public static void main(String[] args) {
		// builder 패턴
		// 점층적 생성자 패턴과 자바빈 패턴의 장점을 결합한 패턴
		// 새로운 객체가 필요한 곳에서 직접 생성하기 전에 필수 인자 값들을 전달하여
		// builder 객체를 만든 후 빌더 객체에 정의된 메서드를 호출해서 객체를 생성함
		Student std1 = new Student.Builder(1)
                                  .name("홍길동")
                                  .dept("컴퓨터공학과")
                                  .age(20)
                                  .build();
		
		System.out.println(std1);
							
	}

}

위와 같이 빌더 패턴을 정의하여 직접 Student 객체를 생성하기 전에 인자 값들을 전달하여 builder 객체를 생성한 후 빌더 객체에 정의된 메서드를 호출해서 객체를 생성하는 방식입니다.

 

build() 메서드를 통해서 Student 객체를 생성합니다.

 

References

source code : https://github.com/yonghwankim-dev/java_study/tree/main/other/constructor/constructor_01_builder_pattern
https://bbaktaeho-95.tistory.com/72