빌더 패턴

정선호·2023년 5월 10일
0

Design Patterns

목록 보기
10/24

관련 영상

빌더 패턴

위키피디아
설명 및 스도코드

  • 복합 객체의 생성 과정과 표현 방법을 분리하여 동일한 생성 절차에서 서로 다른 표현 결과를 만들 수 있게 하는 패턴
    • 생성자 오버로딩이라 할 수 있다
  • 복잡합 유형의 오브젝트를 작성할 때 도움된다.
  • 생성할 객체의 종류를 손쉽게 추가, 확장이 가능한 설계.
  • 플레이어 캐릭터 별개의 옷, 무기 등등을 조합하고 장착하는 등등 여러가지 경우에 상요될 수 있다.

빌더 패턴의 구조

  • 빌더 인터페이스(Builder Interface)
    • 모든 유형의 빌더들에 공통적인 제품 생성 단계들을 선언
  • 구상 빌더(Concrete Builder)
    • 생성 단계들의 다양한 구현을 제공
    • 구상 빌더들은 공통 인터페이스를 따르지 않는 제품들도 생산할 수 있음
  • 제품(Product)
    • 빌드의 결과로 나온 객체들
    • 다른 빌더들에 의해 생성된 제품들은 같은 클래스 계층구조 또는 인터페이스에 속할 필요가 없음
  • 디렉터(Director) 클래스
    • 생성 단계들을 호출하는 순서를 정의
    • 제품들의 특정 설정을 만들고 재사용할 수 있음
  • 클라이언트(Client)
    • 빌더 객체들 중 하나를 디렉터와 연결해야 함
    • 일반적으로 디렉터 생성자의 매개변수들을 통해 한 번만 수행되며, 그 후 디렉터는 모든 추가 생성에 해당 빌더 객체들을 사용함
    • 클라이언트가 빌더 객체를 디렉터의 프로덕션 메서드에 전달할 때를 위한 대안적 접근 방식이 존재하는데, 이 경우 디렉터와 함께 무언가를 만들 때마다 다른 빌더 사용 가능

빌더 패턴의 적용

  • 점층적 생성자를 제거하기 위해 사용
    • 아래와 같은 복잡한 생성자를 만드는 것은 메서드 오버로딩을 지원하는 언어들에서만 사용 가능함
class Pizza {
    Pizza(int size) { ... }
    Pizza(int size, boolean cheese) { ... }
    Pizza(int size, boolean cheese, boolean pepperoni) { ... }
    // …
  • 일부 제품의 다른 표현들을 생성할 수 있도록 하고 싶을 때 사용
    • 제품의 다양한 표현의 생성 과정이 세부 사항만 다른 유사한 단계를 포함할 때 적용할 수 있다
    • 기초 빌더 인터페이스는 가능한 모든 생성 단계들을 정의하고 구상 빌더들은 이러한 단계들을 구현하여 제품의 여러 표현을 생성한다. 또 한편 디렉터 클래스는 건설 순서를 안내한다
  • 빌더를 사용하여 복합체 트리들 또는 기타 복잡한 객체들을 생성
    • 빌더 패턴을 사용하면 제품들을 단계별로 생성할 수 있으며, 또 최종 제품을 손상하지 않고 일부 단계들의 실행을 연기할 수 있다. 그리고 재귀적으로 단계들을 호출할 수도 있는데, 이는 객체 트리를 구축해야 할 때 매우 유용하다.
    • 빌더는 생성 단계들을 수행하는 동안 미완성 제품을 노출하지 않으며, 이는 클라이언트 코드가 불완전한 결과를 가져오는 것을 방지한다.

다른 패턴과의 관계

  • 많은 디자인은 복잡성이 낮고 자식 클래스들을 통해 더 많은 커스터마이징이 가능한 팩토리 메서드로 시작해 더 유연하면서도 더 복잡한 추상 팩토리, 프로토타입 또는 빌더 패턴으로 발전해 나간다.
  • 빌더는 복잡한 객체들을 단계별로 생성하는 데 중점을 둔다. 추상 팩토리는 관련된 객체들의 패밀리들을 생성하는 데 중점을 둔다. 추상 팩토리는 제품을 즉시 반환하지만 빌더는 제품을 가져오기 전에 당신이 몇 가지 추가 생성 단계들을 실행할 수 있도록 한다.
  • 복잡한 복합체 패턴 트리를 생성할 때 빌더를 사용할 수 있다. 왜냐하면 빌더의 생성 단계들을 재귀적으로 작동하도록 프로그래밍할 수 있기 때문이다.
    -빌더를 브리지와 조합할 수 있다. 디렉터 클래스는 추상화의 역할을 하고 다양한 빌더들은 구현의 역할을 한다.
  • 추상 팩토리들, 빌더들 및 프로토타입들은 모두 싱글턴으로 구현할 수 있다.

빌더를 이용한 자동차 생성

  • 제품(Product)
// Our final product
public class Vehicle : MonoBehaviour
{
    private VehicleType vehicleType;

    public void setVehicleType(VehicleType vehicleType)
    {
        this.vehicleType = vehicleType;
    }

    public void AddPart(GameObject part, Vector3 localPosition)
    {
        GameObject obj = Instantiate(part) as GameObject;  
        obj.transform.localPosition = localPosition;
        obj.transform.SetParent(this.transform);
    }

    public string GetPartsList()
    {
        string partsList = vehicleType.ToString() + " parts:\n\t";

        Transform[] trs = GetComponentsInChildren<Transform>();
        for (int i = 1; i < trs.Length; i++)
        {
            partsList += trs[i].gameObject.name + " ";
        }

        return partsList;
    }
}
  • 추상 빌더(Builder Interface)
// 'abstract Builder' class
interface IVehicleBuilder
{
    Vehicle getVehicle();

    void BuildFrame(); // 프레임 만들기
    void BuildEngine(); // 엔진 만들기
    void BuildWheels(); // 바퀴 만들기
}

public enum VehicleType
{
    Car,
    MotorCycle
}
  • 구상 빌더(Concrete builder)
    • 각각 자동차와 오토바이의 콘크리트 빌더이다
class CarBuilder : MonoBehaviour, IVehicleBuilder
{
    public GameObject ParentOfVehicle;
    public GameObject frame;
    public GameObject engine;
    public GameObject wheel1;
    public GameObject wheel2;
    public GameObject wheel3;
    public GameObject wheel4;

    private Vehicle _vehicle;

    public Vehicle getVehicle()
    {
        return _vehicle;
    }

    public void makeVehicle()
    {
        //_vehicle = new Vehicle(VehicleType.Car);
        GameObject obj = Instantiate(ParentOfVehicle) as GameObject;
        _vehicle = obj.GetComponent<Vehicle>();
        _vehicle.setVehicleType(VehicleType.Car);
    }

    public void BuildFrame()
    {
        _vehicle.AddPart(frame, Vector3.zero);
    }

    public void BuildEngine()
    {
        _vehicle.AddPart(engine, new Vector3(0, 0.5f, 0));
    }

    public void BuildWheels()
    {
        _vehicle.AddPart(wheel1, new Vector3(0.75f, -0.5f, 0.5f));
        _vehicle.AddPart(wheel2, new Vector3(-0.75f, -0.5f, 0.5f));
        _vehicle.AddPart(wheel3, new Vector3(-0.75f, -0.5f, -0.5f));
        _vehicle.AddPart(wheel4, new Vector3(0.75f, -0.5f, -0.5f));
    }
}

class MotorCycleBuilder : MonoBehaviour, IVehicleBuilder
{
    public GameObject ParentOfVehicle;
    public GameObject frame;
    public GameObject engine;
    public GameObject wheel1;
    public GameObject wheel2;

    private Vehicle _vehicle;

    public Vehicle getVehicle()
    {
        return _vehicle;
    }

    public void makeVehicle()
    {
        //_vehicle = new Vehicle(VehicleType.MotorCycle);
        GameObject obj = Instantiate(ParentOfVehicle) as GameObject;
        _vehicle = obj.GetComponent<Vehicle>();
        _vehicle.setVehicleType(VehicleType.MotorCycle);
    }

    public void BuildFrame()
    {
        _vehicle.AddPart(frame, Vector3.zero);
    }

    public void BuildEngine()
    {
        _vehicle.AddPart(engine, new Vector3(0, 0.5f, 0));
    }

    public void BuildWheels()
    {
        _vehicle.AddPart(wheel1, new Vector3(1.5f, 0, 0));
        _vehicle.AddPart(wheel2, new Vector3(-1.5f, 0, 0));
    }
}
  • 디렉터(Director)
    • 빌더의 종류(IVehecleBuilder)에 따라 다른 함수가 호출된다
// Our 'Director' class.
class Engineer
{
    public void Construct(IVehicleBuilder vehicleBuilder)
    {
        vehicleBuilder.BuildFrame();
        vehicleBuilder.BuildEngine();
        vehicleBuilder.BuildWheels();
    }
}
  • 클라이언트
    • 빌더를 사용하는 주체
public class BuilderUse : MonoBehaviour
{
    void Start()
    {
        // Instantiate the director and builders
        Engineer engineer = new Engineer();
        //CarBuilder carBuilder = new CarBuilder();
        CarBuilder carBuilder = GetComponent<CarBuilder>();
        carBuilder.makeVehicle();
        //MotorCycleBuilder motorCycleBuilder = new MotorCycleBuilder();
        MotorCycleBuilder motorCycleBuilder = GetComponent<MotorCycleBuilder>();
        motorCycleBuilder.makeVehicle();

        // 빌더를 통해 구성해야 할 제품을 입력받아 제품을 구성한다.
        engineer.Construct(carBuilder);
        engineer.Construct(motorCycleBuilder);

        // 최종 생산된 제품을 반환받는다.
        Vehicle car = carBuilder.getVehicle();
        Debug.Log(car.GetPartsList());
        
        Vehicle motorCycle = motorCycleBuilder.getVehicle();
        Debug.Log(motorCycle.GetPartsList());

        // 최종 생산된 제품의 위치를 지정한다.
        car.transform.position = new Vector3(-3f, 0, 0);
        motorCycle.transform.position = new Vector3(3f, 0, 0);
    }
}
profile
학습한 내용을 빠르게 다시 찾기 위한 저장소

0개의 댓글