본문 바로가기

코딩에 도움이 되는 요소/디자인 패턴

객체를 단계적으로 생산하는 패턴!! 빌더 패턴을 알아보자

빌더 패턴이란??

 

  • 복잡한 객체들을 단계적으로 생성할 수 있게 하는 생성 디자인 패턴
  • 같은 제작 코드를 사용하여 객체의 다양한 유형들과 표현을 제작 할 수 있다
  • 빌더 패턴의 구조
    • Builder Interface는 모든 유형의 빌더들에게 적용되는 공통 생성 메서드를 선언한다
    • 구상 빌더에서는 Builder Interface를 상속 받은 이후에 Builder Interface 내부 메서드를 재정의 함은 물론 다양한 추가 메서드를 구현한다
    • 객체들은 다른 빌더들에 의해 생성된 객체들은 같은 클래스 계층 구조 또는 Interface에 속할 필요가 없다
    • 생성 단계들을 호출하는 클래스를 하나의 클래스로 정의하여 객체들의 특정 설정을 만들고 재사용이 가능하게끔 구현을 한다
    • 클라이언트는 빌더 객체들 중 하나를 생성 단계를 호출하는 클래스와 연결하여 호출한다
  • 빌더 패턴의 예시
    1. Builder Interface를 구현하여 전체적으로 구현이 필요한 것들을 함수로 정의하여 Interface를 선언한다
    // 자동차를 예시로 필요한 인터페이스를 구현
    interface Builder {
    
        fun reset()
        fun setSeat(number:Int)
        fun setGPS()
        fun setEngine(engine: String)
        fun setOil(oil: String)
    }
    
    1. 하위 객체를 구체적으로 구현해준다
    //자동차 객체
    class CarBuilder:Builder {
        private lateinit var car:CarBuilder
    
        override fun reset() {
            this.car = CarBuilder()
        }
    
        override fun setOil(oil: String) {
            println("기름 종류 : $oil")
        }
    
        override fun setSeat(number: Int) {
            println("좌석 수 : $number")
        }
    
        override fun setGPS() {
            println("GPS 장착 됨")
        }
    
        override fun setEngine(engine: String) {
            println("엔진 종류 : $engine")
        }
    
        fun getResult():CarBuilder{
            return this.car
        }
    }
    
    //자동차 메뉴얼 객체
    //.. 이하 생략
    
    1. 생성자를 추가해주는 클래스를 생성해준다
    //카니발과 모닝을 만드는 클래스
    class MakeCar {
    
        fun makeCarnival(builder: Builder){
            builder.reset()
            builder.setSeat(9)
            builder.setGPS()
            builder.setEngine("SUV 전용 엔진")
            builder.setOil("경유")
        }
    
        fun makeMorning(builder: Builder){
            builder.reset()
            builder.setSeat(5)
            builder.setEngine("경차 전용 엔진")
            builder.setOil("가솔린")
        }
    }
    
    1. 클라이언트에서 사용하면 끝!!
    fun main() {
        val makeCar = MakeCar()
        val carBuilder = CarBuilder()
    
        makeCar.makeCarnival(carBuilder)
        println("========================")
        makeCar.makeMorning(carBuilder)
    }
    
  • 빌더 패턴의 적용 예시
    1. 점층적 생성자를 제거하기 위해 사용한다
      • 많은 양의 매개 변수가 있는 생성자가 있다고 가정할 때 이것을 한번에 호출하기는 불편하다 따라서 빌더 패턴을 통해서 더 적은 수의 매개변수를 사용하고 더 간결한 생성자를 만들 수 있다
    2. 코드가 일부 객체의 다른 표현을 생성 할 수 있도록 해야 할 경우
      • 객체의 다양한 표현의 생성 과정이 세부 사항만 다른 유사한 단계를 포함 할 때 적용할 수 있다
    3. 복합체 트리들 또른 기타 복합한 객체를 생성할 때
      • 객체를 단계별로 생성이 가능하고 최종 객체를 손상하지 않고 일부 단계의 객체의 실행을 연기 시킬수 있기 때문에 객체 트리를 구축할 때 유용하다
  • 빌더 패턴의 장단점
    • 객체들을 단계별로 생성하거나 생성 단계들을 연기하거나 재귀적으로 단계들을 실행 시킬 수 있다
    • 제품들의 다양한 표현을 만들 때 같은 생성 코드를 재사용할 수 있다
    • 단일 책임 원칙, 제품의 비즈니스 로직에서 복잡한 생성 코드를 고립 시킬 수 있다
    단점
    • 패턴이 여러 개의 새 클래스들을 생성해야 하므로 코드의 전반적인 복잡섭이 증가한다
  • 장점
  • 다른 패턴과의 관계
    • 복잡성이 낮고 자식 클래스들을 통해 더 많은 커스터 마이징이 가능한 펙토리 메서드로 시작해 더 유연하면서도 더 복잡한 빌더 패턴등으로 발전 시킬 수 있다
    • 추상 팩토리 메서드는 객체를 즉시 반환하지만 빌더는 객체를 가져오기 전에 몇가지 추가 생성 단계를 실행 할 수 있도록 한다
    • 빌더 패턴의 생성 단계들은 재귀적으로 작동하도록 프로그래밍이 쉽기 때문에 복잡한 복합체 패턴 트리를 생성할 때 빌더를 사용할 수 있다
    • 브릿지와 조합이 가능하다
    • 빌더 패턴도 싱글턴 패턴으로 구현이 가능하다