[Design Pattern] 빌더 패턴(Builder Pattern)

Jay·2021년 1월 26일
0

Design Pattern

목록 보기
1/1
post-thumbnail

빌더 패턴 ⁉️

  • 복잡한 객체를 생성하는 방법을 정의하는 클래스와 표현하는 방법을 정의하는 클래스를 별도로 분리하여, 서로 다른 표현이라도 이를 생성할 수 있는 동일한 절차를 제공하는 패턴.
  • 빌더 패턴은 생성해야 하는 객체가 Optional한 속성을 많이 가질 때 더 빛을 발휘한다.

생성 패턴의 소속. 빌더 패턴

  • 생성 패턴 = 인스턴스를 만드는 절차를 추상화하는 패턴
  • 생성 패턴 소속의 패턴들은 객체를 생성합성하는 방법 혹은 객체의 표현 방법을 시스템과 분리해준다.
  • 시스템이 상속보다 복합 방법을 사용하는 방향으로 진화되어 가기에 중요하다.

2가지 주요 이슈.

  1. 생성 패턴은 시스템이 어떤 Concrete Class를 사용하는지에 대한 정보를 캡슐화한다.

  2. 생성 패턴은 이들 클래스의 인스턴스들이 어떻게 만들어지고 어떻게 결합하는지에 대한 부분을 완전히 가려준다.

즉, 생성 패턴은 무엇이 생성되고, 누가 생성하고, 어떻게 생성되는지, 언제 생성할 지를 결정하는 유연성을 확보 할 수 있게 된다.


개념적으로 더 살펴보자.

많은 Optional한 멤버 변수나 지속성 없는 상태 값들에 대해 처리해야 하는 문제들을 해결한다.

❌ 팩토리 패턴, 추상 팩토리 패턴에서 생성해야 하는 클래스에 대한 속성 값이 많을 때, 아래와 같은 이슈가 있다.

  • 클라이언트 프로그램으로부터 팩토리 클래스로 많은 파라미터를 넘겨줄 때 타입, 순서 등에 대한 관리가 어려워져 에러가 발생활 확률이 높아진다.
  • 경우에 따라 필요 없는 파라미터들에 대해 팩토리 클래스에 일일이 null값을 넘겨줘야 한다.
  • 생성해야 하는 sub class가 무거워지고 복잡해짐에 따라 팩토리 클래스 또한 복잡해진다.

⭕️ 빌더 패턴은 이러한 문제 해결을 위해 별도의 Builder 클래스를 만들어
필수 값에 대해서는 생성자를 통해,
선택적인 값들에 대해서는 메소드를 통해
stey-by-step으로 값을 입력 받은 후에 build() 메소드를 통해 최종적으로 하나의 인스턴스를 리턴하는 방식이다.

👋 자주 사용되는 패턴 중 하나로,
Retrofit, OkHttp 등 유명 오픈 소스에서도 빌더 패턴을 사용한다.

구현 방법

  1. 빌더 클래스를 Static Nested Class로 생성한다.
    이때, 관례적으로 생성하고자 하는 클래스 이름 뒤에 Builder를 붙인다.
    ex. Computer class -> ComputerBuilder

  2. 빌더 클래스의 생성자는 public으로 하며, 필수 값들에 대해 생성자의 파라미터로 받는다.

  3. Optional한 값들에 대해서는 각각의 속성마다 메소드로 제공하며, 이때 중요한 것은 메소드의 리턴 값이 빌더 객체 자신이어야 한다.

  4. 마지막 단계, 빌더 클래스 내에 build()메소드를 정의하여 클라이언트 프로그램에게 최종 생성된 결과물을 제공해야 한다.
    이렇게 build()를 통해서만 객체 생성을 제공하기에 생성 대상이 되는 클래스의 생성자는 private으로 정의해야 한다.

ComputerBuilder Example

public class Computer {
	
    //required parameters
    private String HDD;
    private String RAM;
	
    //optional parameters
    private boolean isGraphicsCardEnabled;
    private boolean isBluetoothEnabled;
	
 
    public String getHDD() {
        return HDD;
    }
 
    public String getRAM() {
        return RAM;
    }
 
    public boolean isGraphicsCardEnabled() {
        return isGraphicsCardEnabled;
    }
 
    public boolean isBluetoothEnabled() {
        return isBluetoothEnabled;
    }
	
    // 여기 🛑
    // 클래스의 생성자는 private 하게. 구현 방법 4번 ✅
    private Computer(ComputerBuilder builder) {
        this.HDD=builder.HDD;
        this.RAM=builder.RAM;
        this.isGraphicsCardEnabled=builder.isGraphicsCardEnabled;
        this.isBluetoothEnabled=builder.isBluetoothEnabled;
    }
	
    //Builder Class
    // static이 포인트. 구현 방법 1번 ✅
    // 빌더 클래스 생성자는 public으로 한다. 구현 방법 2번 ✅
    public static class ComputerBuilder{
 
        // required parameters        
        private String HDD;
        private String RAM;
 
        // optional parameters
        private boolean isGraphicsCardEnabled;
        private boolean isBluetoothEnabled;
		
        // 필수값들은 파라미터로 받는다. 구현 방법 2번 ✅
        public ComputerBuilder(String hdd, String ram){
            this.HDD=hdd;
            this.RAM=ram;
        }
 
        public ComputerBuilder setGraphicsCardEnabled(boolean isGraphicsCardEnabled) {
            this.isGraphicsCardEnabled = isGraphicsCardEnabled;
            return this;
        }
 
 		// 옵션 값들은 속상마다 메소드로 제공한다.
        // 리턴값이 빌더 객체 자신이다. 구현 방법 3번 ✅
        public ComputerBuilder setBluetoothEnabled(boolean isBluetoothEnabled) {
            this.isBluetoothEnabled = isBluetoothEnabled;
            return this;
        }
		       
        public Computer build(){
            return new Computer(this);
        }
 
    }
 
}

중요한 점

Computer 클래스가 setter메소드 없이 오직 getter메소드만 가진다는 것과 public 생성자가 없다. (올라가서 빨간 표시🛑를 찾아보자. public 생성자가 아니다.)
그렇기에 Computer객체를 얻으로면 오직 ComputerBuilder클래스를 이용하여야 한다.

어떻게 사용할까?

public class TestBuilderPattern {
 
    public static void main(String[] args) {
    	
        // Computer 객체를 얻기 위해 ComputerBuilder를 이용한다.
        Computer comp = new Computer.ComputerBuilder("500 GB", "2 GB") //필수값은 파라미터로 넣어줌.
                .setBluetoothEnabled(true) //옵션값은 메서드로 제공.
                .setGraphicsCardEnabled(true)
                .build();
    }
 
}
profile
developer

0개의 댓글