-
Builder 디자인 패턴자바 2024. 2. 13. 15:15
Builder Pattern이란?
빌더 패턴(Builder Pattern)은 복잡한 객체의 생성 과정과 표현 방법을 분리하여 다양한 구성의 인스턴스를 만드는 생성 패턴이다.
이해하기 쉬운 사례로 수제 햄버거를 들 수 있다. 수제 햄버거를 주문할때 빵이나 패티 등 속재료들은 주문하는 사람이 마음으로 결정된다. 어느 사람은 치즈를 빼달라고 할수 있고 어느 사람은 토마토를 빼달라고 할수 있다. 이처럼 선택적 속재료들을 보다 유연하게 받아 다양한 타입의 인스턴스를 생성할수 있어, 클래스의 선택적 매개변수가 많은 상황에서 유용하게 사용된다.
빌더패턴의 탄생 배경
점층적 생성자 패턴
점층적 생성자 패턴(Telescoping Constructor Pattern)은 필수 매개변수와 함께 선택 매개변수를 0개, 1개, 2개 .. 받는 형태로, 우리가 다양한 매개변수를 입력받아 인스턴스를 생성하고 싶을때 사용하던 생성자를 오버로딩 하는 방식이다.
class Hamburger { // 필수 매개변수 private int bun; private int patty; // 선택 매개변수 private int cheese; private int lettuce; private int tomato; private int bacon; public Hamburger(int bun, int patty, int cheese, int lettuce, int tomato, int bacon) { this.bun = bun; this.patty = patty; this.cheese = cheese; this.lettuce = lettuce; this.tomato = tomato; this.bacon = bacon; } public Hamburger(int bun, int patty, int cheese, int lettuce, int tomato) { this.bun = bun; this.patty = patty; this.cheese = cheese; this.lettuce = lettuce; this.tomato = tomato; } public Hamburger(int bun, int patty, int cheese, int lettuce) { this.bun = bun; this.patty = patty; this.cheese = cheese; this.lettuce = lettuce; } public Hamburger(int bun, int patty, int cheese) { this.bun = bun; this.patty = patty; this.cheese = cheese; } ... } public static void main(String[] args) { // 모든 재료가 있는 햄버거 Hamburger hamburger1 = new Hamburger(2, 1, 2, 4, 6, 8); // 빵과 패티 치즈만 있는 햄버거 Hamburger hamburger2 = new Hamburger(2, 1, 1); // 빵과 패티 베이컨만 있는 햄버거 Hamburger hamburger3 = new Hamburger(2, 0, 0, 0, 0, 6); }
타입이 다양할 수록 생성자 메서드 수가 기하급수적으로 늘어나 가독성이나 유지보수 측면에서 좋지 않다.
자바 빈(Java Beans) 패턴
이러한 단점을 보완하기 위해 Setter 메소드를 사용한 자바 빈(Bean) 패턴이 고안 되었다. 매개변수가 없는 생성자로 객체 생성후 Setter 메소드를 이용해 클래스 필드의 초깃값을 설정하는 방식이다.
class Hamburger { // 필수 매개변수 private int bun; private int patty; // 선택 매개변수 private int cheese; private int lettuce; private int tomato; private int bacon; public Hamburger() {} public void setBun(int bun) { this.bun = bun; } public void setPatty(int patty) { this.patty = patty; } public void setCheese(int cheese) { this.cheese = cheese; } public void setLettuce(int lettuce) { this.lettuce = lettuce; } public void setTomato(int tomato) { this.tomato = tomato; } public void setBacon(int bacon) { this.bacon = bacon; } } public static void main(String[] args) { // 모든 재료가 있는 햄버거 Hamburger hamburger1 = new Hamburger(); hamburger1.setBun(2); hamburger1.setPatty(1); hamburger1.setCheese(2); hamburger1.setLettuce(4); hamburger1.setTomato(6); hamburger1.setBacon(8); // 빵과 패티 치즈만 있는 햄버거 Hamburger hamburger2 = new Hamburger(); hamburger2.setBun(2); hamburger2.setPatty(1); hamburger2.setCheese(2); // 빵과 패티 베이컨만 있는 햄버거 Hamburger hamburger3 = new Hamburger(); hamburger3.setBun(2); hamburger2.setPatty(1); hamburger3.setBacon(8); }
기존 생성자 오버로딩에서 나타났던 가독성 문제점이 사라지고 선택적인 파라미터에 대해 해당되는 Setter 메서드를 호출함으로써 유연적으로 객체 생성이 가능해졌다. 하지만 이러한 방식은 객체 생성 시점에 모든 값들을 주입 하지 않아 일관성(consistency) 문제와 불변성(immutable) 문제가 나타나게 된다.
빌더(Builder) 패턴
빌더 패턴은 이러한 문제들을 해결하기 위해 별도의 Builder 클래스를 만들어 메소드를 통해 step-by-step 으로 값을 입력받은 후에 최종적으로 build() 메소드로 하나의 인스턴스를 생성하여 리턴하는 패턴이다.
public static void main(String[] args) { // 생성자 방식 Hamburger hamburger = new Hamburger(2, 3, 0, 3, 0, 0); // 빌더 방식 Hamburger hamburger = new Hamburger.Builder(10) .bun(2) .patty(3) .lettuce(3) .build(); }
빌더 패턴을 이용하면 더이상 생성자 오버로딩 열거를 하지 않아도 되며, 데이터의 순서에 상관없이 객체를 만들어내 생성자 인자 순서를 파악할 필요도 없고 잘못된 값을 넣는 실수도 하지 않게 된다. 점층적 생성자 패턴과 자바빈즈 패턴 두 가지의 장점만을 취하였다고 볼 수 있다.
빌더 패턴 장점
1. 객체 생성 과정을 일관된 프로세스로 표현
생성자 방식으로 객체를 생성하는 경우는 매개변수가 많아질수록 가독성이 급격하게 떨어진다. 클래스 변수가 4개 이상만 되어도 각 인자 순서 마다 이 값이 어떤 멤버에 해당되는지 바로 파악이 힘들다.
반면 다음과 같이 빌더 패턴을 적용하면 직관적으로 어떤 데이터에 어떤 값이 설정되는지 한눈에 파악할 수 있게 된다. 특히 연속된 동일 타입의 매개 변수를 많이 설정할 경우에 발생할 수 있는 설정 오류와 같은 실수를 방지할 수 있다.
2. 필수 멤버와 선택적 멤버를 분리 가능
빌더 클래스를 통해 초기화가 필수인 멤버는 빌더의 생성자로 받게 하여 필수 멤버를 설정해주어야 빌더 객체가 생성되도록 유도하고, 선택적인 멤버는 빌더의 메서드로 받도록 하면, 사용자로 하여금 필수 멤버와 선택 멤버를 구분하여 객체 생성을 유도할 수 있다.
3. 멤버에 대한 변경 가능성 최소화를 추구
불변 객체로 구성하면 다른 사람이 개발한 함수를 위험없이 이용을 보장할 수 있어 협업에도 유지보수에도 유용하다. 빌더 패턴은 생성자 없이 어느 객체에 대해 '변경 가능성을 최소화' 를 추구하여 불변성을 갖게 해주게 되는 것이다.
빌더 패턴 단점
1. 코드 복잡성 증가
우선 빌더 패턴을 적용하려면 N개의 클래스에 대해 N개의 새로운 빌더 클래스를 만들어야 해서, 클래수 수가 기하급수적으로 늘어나 관리해야 할 클래스가 많아지고 구조가 복잡해질 수 있다. 또한 선택적 매개변수를 많이 받는 객체를 생성하기 위해서는 먼저 빌더 클래스부터 정의해야한다. 다만 이부분은 여느 디자인 패턴이 가지는 단점이기도 하다.
2. 생성자 보다는 성능은 떨어진다.
매번 메서드를 호출하여 빌더를 거쳐 인스턴스화 하기 때문에 어쩌면 당연한 말일지도 모른다. 비록 생성 비용 자체는 크지는 않지만, 어플리케이션의 성능을 극으로 중요시되는 상황이라면 문제가 될수 있다.
출처: https://inpa.tistory.com/entry/GOF-💠-빌더Builder-패턴-끝판왕-정리 [Inpa Dev 👨💻:티스토리]
'자바' 카테고리의 다른 글
SQL 튜닝 (3) 2024.03.05 전략 디자인 패턴 (0) 2024.02.06 TDD (2) 2024.01.22 Spring boot3에서 Swagger Springdoc 사용하기 - maven (0) 2024.01.09 java 예외처리 throws와 try ~ catch 차이점 (0) 2023.12.13