컴포지트 패턴은 예시를 통해 설명하겠다.




위의 그림과 같은 인벤토리 창이 있는 RPG게임이 있다고 해보자. 아이템 슬롯에는 포션이나 무기 같은 아이템을 보관할 수 있는데, 아이템 중에 4차원 가방이라는 아이템이 있다. 그 가방은 자신도 아이템이라서 아이템 슬롯에 위치하는데, 그 가방을 더블클릭하면 또다른 아이템 슬롯 창이 뜨고, 거기에도 아이템을 보관할 수 있다고 해보자. 이런 구조를 컴포지트 패턴을 이용해 설계한다면 아래와 같이 만들 수 있다.






위의 클래스 구조를 간략히 설명하면 다음과 같다.


클래스 명

 설명

 컴포지트 패턴 내에서의 용어

 Item

 모든 아이템은 이 추상클래스를 상속받아 만들어진다.

 Component

 ItemLeaf

 4차원 가방을 제외한 다른 아이템들은 ItemLeaf를 상속받아 만들어진다. Leaf는 잎, 말단이란 뜻으로, 상위클래스인 Item과 구별해주기 위해 접미사로 붙여주었다.

 Leaf(단일객체)

 ItemBag

 4차원 가방 아이템. 이 아이템은 자신도 Item이면서 다른 아이템을 List에 넣어 관리할 수 있다. 자기 자신이 추가 인벤토리 아이템 슬롯인 셈.

 Composite(복합객체)



type은 물약같은 사용 아이템인지, 무기같은 장착 아이템인지 구별해주는 멤버변수인데, 뭔가 허전해서 넣어봤을 뿐이며 자신의 게임에 맞게 수정하거나 빼버리면 될 것이다.




자세한 코드는 아래와 같다.


//최상위 아이템 추상클래스
public abstract class Item
{
    public enum Type
    {
        Use,
        Equip,
        Own,
        Main
    }

    public string name;
    public Type type;

    //아이템이 수행할 동작을 여기에 정의하거나 실행.
    public abstract void Operate();

    //생성자
    public Item(string name, Type type)
    {
        this.name = name;
        this.type = type;
        Debug.Log("부모클래스 세팅 : " + name + " " + type.ToString());
    }
}


//4차원 가방 클래스
public class ItemBag : Item
{
    private List itemList = new List();

    public ItemBag(string name, Type type = Type.Use) : base(name, type)
    {
        
    }

    public override void Operate()
    {
        OpenSlotWindow();
    }

    //가방의 인벤토리 창을 여는 멤버함수. 본 예제에서는 내부를 정의하진 않겠다.
    private void OpenSlotWindow()
    {

    }

    //가방에 아이템 넣을때 사용하는 함수
    public void Add(Item item)
    {
        if(itemList.Contains(item))
        {
            Debug.Log(" 버그 : 이미 해당 아이템이 리스트에 존재함.");
            return;
        }
        itemList.Add(item);
    }

    //가방에서 아이템 뺄때 사용하는 함수
    public void Remove(Item item)
    {
        if(!itemList.Contains(item))
        {
            Debug.Log("버그 : 해당 아이템이 리스트에 존재하지 않음.");
            return;
        }
        itemList.Remove(item);
    }
}


//일반 아이템 클래스
public class ItemLeaf : Item
{
    //생성자
    public ItemLeaf(string name, Type type) : base(name, type)
    {
        
    }

    public override void Operate()
    {
        
    }
}


//추가 예시. 물약을 만들어보았다.
public class Potion : ItemLeaf
{
    public Potion(string name, Type type) : base(name, type)
    {
        
    }

    //또 오버라이딩 해준다. 그럼 ItemLeaf의 Operate함수는 실행되지 않을 것이다.
    public override void Operate()
    {
        UsePotion();
    }

    //체력을 채우고, 물약 갯수를 1 감소시켜주는 기능을 함수내부에 만들어주면 될 것이다.
    private void UsePotion()
    {

    }
}

주의사항
: 본 예제는 컴포지트 패턴의 설명을 돕기 위해 만든 설명용 예제일 뿐이다. 실제 게임에 적용하려면 많은 고민과 수정을 거쳐야 할 것이며, 아이템 구조를 짤 때 컴포지트 패턴을 사용하지 않을 수도 있다.


+ Recent posts