복합 객체(Composite) 와 단일 객체(Leaf)를 동일한 컴포넌트로 취급하여, 클라이언트에게 이 둘을 구분하지 않고 동일한 인터페이스를 사용하도록 하는 구조 패턴
- Component : ItemComponent
- Leaf : Item
- Composite : ItemGroup
#include <iostream> #include <string> #include <vector> enum class ItemType { Consumables, Equipment, Misc }; // 개별 객체(Leaf)와 집합 객체(Composite)를 묶는 공통 인터페이스 (Component) class ItemComponent { public: virtual ~ItemComponent() = default; virtual void GetItem() = 0; }; // 개별 객체를 나타내는 클래스 (Leaf) class Item : public ItemComponent { private: std::string name; public: Item(const std::string& _name) : name(_name) {} void GetItem() override { std::cout << " 아이템 : " << name << std::endl; } }; // 집합 객체를 나타내는 클래스 (Composite) class ItemGroup : public ItemComponent { private: std::string name; std::vector<std::unique_ptr<ItemComponent>> items; public: ItemGroup(const std::string& _name) : name(_name) {} void AddItem(std::unique_ptr<ItemComponent> _item) { items.push_back(std::move(_item)); } void GetItem() override { std::cout << name << std::endl; // 자신이 포함하는 모든 자식들의 GetItem()을 재귀적으로 호출 for (const auto& item : items) { item->GetItem(); } } }; // 컴포지트 패턴을 사용하는 클라이언트 클래스 class Inventory { private: std::unique_ptr<ItemGroup> consumables; std::unique_ptr<ItemGroup> equipment; std::unique_ptr<ItemGroup> misc; public: Inventory() : consumables(std::make_unique<ItemGroup>("<소모품>")), equipment(std::make_unique<ItemGroup>("<장비>")), misc(std::make_unique<ItemGroup>("<기타>")) {} void AddItem(std::unique_ptr<ItemComponent> _item, const ItemType& _type) { switch (_type) { case ItemType::Consumables: consumables->AddItem(std::move(_item)); break; case ItemType::Equipment: equipment->AddItem(std::move(_item)); break; case ItemType::Misc: misc->AddItem(std::move(_item)); break; } } void GetItem() { std::cout << "<<인벤토리>>" << std::endl; consumables->GetItem(); equipment->GetItem(); misc->GetItem(); } }; int main() { Inventory inven; inven.AddItem(std::make_unique<Item>("검"), ItemType::Equipment); inven.AddItem(std::make_unique<Item>("활"), ItemType::Equipment); inven.AddItem(std::make_unique<Item>("방패"), ItemType::Equipment); inven.AddItem(std::make_unique<Item>("체력물약"), ItemType::Consumables); inven.AddItem(std::make_unique<Item>("마나물약"), ItemType::Consumables); inven.AddItem(std::make_unique<Item>("버프물약"), ItemType::Consumables); inven.AddItem(std::make_unique<Item>("돌"), ItemType::Misc); inven.AddItem(std::make_unique<Item>("풀"), ItemType::Misc); inven.AddItem(std::make_unique<Item>("흙"), ItemType::Misc); inven.GetItem(); return 0; }
- 통일된 인터페이스 : Item과 ItemGroup이 모두 ItemComponent를 상속받아 GetItem() 메서드를 가지고있다. 이 덕분에 클라이언트(Inventory)는 객체가 단일 아이템인지, 아이템 그룹인지 신경 쓸 필요 없이 동일한 방식으로 다룰 수 있다.
- 재귀적 구조 : ItemGroup의 GetItem() 메서드는 자신이 가진 자식 요소들의 GetItem()을 다시 호출, 만약 자식이 또 다른 ItemGroup이라면 이 과정이 재귀적으로 반복되어 전체 트리 구조를 순회할 수 있다.
- 유연한 구조: Inventory -> ItemGroup ("소모품", "장비", "기타") -> Item ("검", "활", "방패"...) 으로 이어지는 계층적인 구조를 매우 쉽게 표현하고 관리할 수 있다.