Unity

[디자인 패턴] 생성 패턴 - 빌더

CCS_Cheese 2025. 10. 14. 21:18

이번 게시글에서는 생성 패턴 중 하나인 빌더 패턴에 대해서 이야기해보려고 합니다.

아래와 같은 문제 상황이 있다고 가정하여, 이를 해결하는 디자인 패턴의 구조를 작성하고, 실제 스크립트를 구현해 보겠습니다.

 

문제 상황

많은 멤버 변수를 필드로 가지는 특정 클래스의 객체를 생성한다고 하였을 때, 각 멤버 변수마다의 초기화 값을 생성자를 통해 입력해야 합니다. 이때 생성되는 객체별로, 사용하지 않는 멤버 변수들이 종종 있을 수 있지만, 생성자를 통해 초기화를 진행하게 되면 생성자에 포함되어 있는 파라미터들을 전부 넣어줘야 합니다.

빌더 패턴이란?

빌더 패턴(Builder Pateern)은 복잡한 객체의 생성 과정과 표현 방법을 다양한 구성의 인스턴스를 만드는 생성 패턴입니다.
기존 생성자에 들어갈 매개변수들을 메서드 화하여, 마지막에 통합 빌드하여 객체를 생성하는 방식입니다.

 

 

문제 해결

위에서도 언급했듯이, 생성자를 통한 전체적인 인스턴스의 초기화를 진행하는 것이 아닌, 인스턴스의 필요한 부분 별로, 메서드화 하여, 이를 조립하듯이 인스턴스를 생성한다고 생각하면 됩니다. 아래 클래스 다이어그램을 통해 자세히 확인해 보겠습니다.

클래스 다이어그램

이번 빌더 패턴 또한 Monster를 활용하여 다이어그램을 작성해 보았는데. 몬스터의 초기화 방식에 점점 많은 파라미터의 생성자를 활용하게 된 다했을 때 그림과 같이 활용이 가능합니다. 실제 Monster 객체를 생성하고 싶은 Client에서 객체 생성을 총괄 담당하는 Directory를 통해 특정 Builder를 주입하고, 이를 통해 사전에 정의된 특정 객체들의 메서드를 활용하고, 내부에서 하나씩 조립하여, 객체를 생성하는 방식으로 구현이 가능합니다.

 

클래스 정의

NameSpace client
// Builder의 Directory 설정
public class BuilderSamples : MonoBehaviour
{
    [SerializeField] private MonsterDirector Director;
    private IMonsterBuilder builder;
    void Start()
    {
        builder = new MonsterBuilder();
        Director.SetBuilder(builder);
        Director.MakeIceMonster();
        Director.MakeNormalMonster();
    }
}
오브젝트를 생성하려는 위치에서, 빌더를 생성하고, 빌더를 통해, 각 오브젝트들을 내부에서 생성합니다. 그 뒤 생성하려는 오브젝트에 맞추어, 몬스터의 세부 특성을 생성자가 아닌, 메서드 방식을 통해 각 오브젝트를 인스턴스 합니다. 아래와 같은 MakeIceMonster, MakeNormalMonster를 통해 이해할 수 있습니다.
NameSpace BuilderMaker
// Monster 생성 디렉터
public class MonsterDirector : MonoBehaviour
{ 
    private IMonsterBuilder monsterBuilder;

    public NormalMonster NormalMonsterPrefab;
    public IceMonster IceMonsterPrefab;
    public void SetBuilder(IMonsterBuilder builder)
    {
        monsterBuilder = builder;
    }

    public Monster MakeNormalMonster()
    {
        monsterBuilder.Init(Instantiate(NormalMonsterPrefab));
        monsterBuilder.SetHealth(100);
        monsterBuilder.SetSpeed(10);
        monsterBuilder.SetStrength(20);
        return monsterBuilder.GetResult();
    }

    public Monster MakeIceMonster()
    {
        monsterBuilder.Init(Instantiate(IceMonsterPrefab));
        monsterBuilder.SetHealth(200);
        monsterBuilder.SetSpeed(20);
        monsterBuilder.SetStrength(30);
        return monsterBuilder.GetResult();
    } 
}
Health, Speed, Strength 등 몬스터에 사용될 다양한 스텟을 Init 할 때 기존에 생성자를 활용하면 생성자의 파라미터가 많아지게 되고, 이를 각 몬스터에 특성에 맞게 사용될 파라미터와 사용되지 않는 파라미터를 메서드 형태로 분리하여, 생성되는 몬스터에 맞추어 데이터를 Init할 수 있게 변경이 가능합니다.

 

NameSpace Builde
public class MonsterBuilder : IMonsterBuilder
{
    private Monster monster;

    public void Init(Monster monster)
    {
        this.monster = monster;
    }
    public void SetHealth(float health)
    {
        monster.Health = health;
    }

    public void SetSpeed(float speed)
    {
        monster.Speed = speed;
    }

    public void SetStrength(float strength)
    {
        monster.Strength = strength;
    }

    public Monster GetResult()
    {
        return monster;
    }
}

public interface IMonsterBuilder
{
    void Init(Monster monster);
    void SetHealth(float health);
    void SetSpeed(float speed);
    void SetStrength(float strength);

    Monster GetResult();
}
몬스터 객체를 생성할 때 초기 Init을 진행하여, Base 몬스터를 설정(Ice, Normal)하고, 그에 따른 객체를 생성하는데, 이때 파라미터를 통해 각 필드의 값을 전달하는 것이 아닌, 메서드를 통해 필요한 값만 초기화를 진행하여, 마지막 GetResult를 통한 생성된 객체를 반환하게 됩니다.

Unity에서 실행하였을 때 생성되는 구조

Ice, Normal Monster
BuilderPattern.unitypackage
0.01MB