관련 영상
빌더 패턴
위키피디아
설명 및 스도코드
- 복합 객체의 생성 과정과 표현 방법을 분리하여 동일한 생성 절차에서 서로 다른 표현 결과를 만들 수 있게 하는 패턴
- 복잡합 유형의 오브젝트를 작성할 때 도움된다.
- 생성할 객체의 종류를 손쉽게 추가, 확장이 가능한 설계.
- 플레이어 캐릭터 별개의 옷, 무기 등등을 조합하고 장착하는 등등 여러가지 경우에 상요될 수 있다.
빌더 패턴의 구조
- 빌더 인터페이스(Builder Interface)
- 모든 유형의 빌더들에 공통적인 제품 생성 단계들을 선언
- 구상 빌더(Concrete Builder)
- 생성 단계들의 다양한 구현을 제공
- 구상 빌더들은 공통 인터페이스를 따르지 않는 제품들도 생산할 수 있음
- 제품(Product)
- 빌드의 결과로 나온 객체들
- 다른 빌더들에 의해 생성된 제품들은 같은 클래스 계층구조 또는 인터페이스에 속할 필요가 없음
- 디렉터(Director) 클래스
- 생성 단계들을 호출하는 순서를 정의
- 제품들의 특정 설정을 만들고 재사용할 수 있음
- 클라이언트(Client)
- 빌더 객체들 중 하나를 디렉터와 연결해야 함
- 일반적으로 디렉터 생성자의 매개변수들을 통해 한 번만 수행되며, 그 후 디렉터는 모든 추가 생성에 해당 빌더 객체들을 사용함
- 클라이언트가 빌더 객체를 디렉터의 프로덕션 메서드에 전달할 때를 위한 대안적 접근 방식이 존재하는데, 이 경우 디렉터와 함께 무언가를 만들 때마다 다른 빌더 사용 가능
빌더 패턴의 적용
- 점층적 생성자를 제거하기 위해 사용
- 아래와 같은 복잡한 생성자를 만드는 것은 메서드 오버로딩을 지원하는 언어들에서만 사용 가능함
class Pizza {
Pizza(int size) { ... }
Pizza(int size, boolean cheese) { ... }
Pizza(int size, boolean cheese, boolean pepperoni) { ... }
// …
- 일부 제품의 다른 표현들을 생성할 수 있도록 하고 싶을 때 사용
- 제품의 다양한 표현의 생성 과정이 세부 사항만 다른 유사한 단계를 포함할 때 적용할 수 있다
- 기초 빌더 인터페이스는 가능한 모든 생성 단계들을 정의하고 구상 빌더들은 이러한 단계들을 구현하여 제품의 여러 표현을 생성한다. 또 한편 디렉터 클래스는 건설 순서를 안내한다
- 빌더를 사용하여 복합체 트리들 또는 기타 복잡한 객체들을 생성
- 빌더 패턴을 사용하면 제품들을 단계별로 생성할 수 있으며, 또 최종 제품을 손상하지 않고 일부 단계들의 실행을 연기할 수 있다. 그리고 재귀적으로 단계들을 호출할 수도 있는데, 이는 객체 트리를 구축해야 할 때 매우 유용하다.
- 빌더는 생성 단계들을 수행하는 동안 미완성 제품을 노출하지 않으며, 이는 클라이언트 코드가 불완전한 결과를 가져오는 것을 방지한다.
다른 패턴과의 관계
- 많은 디자인은 복잡성이 낮고 자식 클래스들을 통해 더 많은 커스터마이징이 가능한 팩토리 메서드로 시작해 더 유연하면서도 더 복잡한 추상 팩토리, 프로토타입 또는 빌더 패턴으로 발전해 나간다.
- 빌더는 복잡한 객체들을 단계별로 생성하는 데 중점을 둔다. 추상 팩토리는 관련된 객체들의 패밀리들을 생성하는 데 중점을 둔다. 추상 팩토리는 제품을 즉시 반환하지만 빌더는 제품을 가져오기 전에 당신이 몇 가지 추가 생성 단계들을 실행할 수 있도록 한다.
- 복잡한 복합체 패턴 트리를 생성할 때 빌더를 사용할 수 있다. 왜냐하면 빌더의 생성 단계들을 재귀적으로 작동하도록 프로그래밍할 수 있기 때문이다.
-빌더를 브리지와 조합할 수 있다. 디렉터 클래스는 추상화의 역할을 하고 다양한 빌더들은 구현의 역할을 한다.
- 추상 팩토리들, 빌더들 및 프로토타입들은 모두 싱글턴으로 구현할 수 있다.
빌더를 이용한 자동차 생성
// 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;
}
}
// 'abstract Builder' class
interface IVehicleBuilder
{
Vehicle getVehicle();
void BuildFrame(); // 프레임 만들기
void BuildEngine(); // 엔진 만들기
void BuildWheels(); // 바퀴 만들기
}
public enum VehicleType
{
Car,
MotorCycle
}
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);
}
}