C++ 구조 패턴 — Adapter·Decorator·Composite
구조 패턴 개요
섹션 제목: “구조 패턴 개요”구조 패턴(Structural Pattern)은 클래스와 객체를 더 큰 구조로 조합하는 방법을 다룹니다. 기존 코드를 수정하지 않고 기능을 확장하거나 인터페이스를 변환합니다.
Adapter 패턴
섹션 제목: “Adapter 패턴”호환되지 않는 인터페이스를 연결합니다. 서드파티 라이브러리나 레거시 코드를 현재 시스템에 연결할 때 사용합니다.
// 기존 물리 엔진 인터페이스 (변경 불가)class PhysicsEngine{public: void ApplyForceXYZ(float fx, float fy, float fz) { std::cout << "Force: " << fx << ", " << fy << ", " << fz << "\n"; }};
// 게임에서 사용하는 벡터 타입struct Vec3 { float x, y, z; };
// 게임이 원하는 인터페이스class IPhysicsComponent{public: virtual void ApplyForce(Vec3 force) = 0; virtual ~IPhysicsComponent() = default;};
// 어댑터: PhysicsEngine을 IPhysicsComponent로 변환class PhysicsAdapter : public IPhysicsComponent{public: explicit PhysicsAdapter(PhysicsEngine& engine) : _engine(engine) {}
void ApplyForce(Vec3 force) override { _engine.ApplyForceXYZ(force.x, force.y, force.z); }
private: PhysicsEngine& _engine;};
// 사용: 게임 코드는 IPhysicsComponent만 알면 됨PhysicsEngine engine;PhysicsAdapter adapter(engine);IPhysicsComponent& physics = adapter;
physics.ApplyForce({0.0f, 9.8f, 0.0f});Decorator 패턴
섹션 제목: “Decorator 패턴”객체에 동적으로 기능을 추가합니다. 상속 없이 조합으로 기능을 확장합니다.
// 기본 컴포넌트 인터페이스class IWeapon{public: virtual int GetDamage() const = 0; virtual std::string GetName() const = 0; virtual ~IWeapon() = default;};
// 기본 무기class BasicSword : public IWeapon{public: int GetDamage() const override { return 10; } std::string GetName() const override { return "Sword"; }};
// 데코레이터 기반 클래스class WeaponDecorator : public IWeapon{public: explicit WeaponDecorator(std::unique_ptr<IWeapon> weapon) : _weapon(std::move(weapon)) {}
protected: std::unique_ptr<IWeapon> _weapon;};
// 불꽃 인챈트 데코레이터class FireEnchant : public WeaponDecorator{public: using WeaponDecorator::WeaponDecorator;
int GetDamage() const override { return _weapon->GetDamage() + 15; // 화염 피해 추가 }
std::string GetName() const override { return _weapon->GetName() + " of Fire"; }};
// 독 인챈트 데코레이터class PoisonEnchant : public WeaponDecorator{public: using WeaponDecorator::WeaponDecorator;
int GetDamage() const override { return _weapon->GetDamage() + 8; }
std::string GetName() const override { return _weapon->GetName() + " of Poison"; }};
// 데코레이터 체인auto weapon = std::make_unique<BasicSword>();// Sword(10) → Fire(+15) → Poison(+8) = 33auto enchanted = std::make_unique<PoisonEnchant>( std::make_unique<FireEnchant>(std::move(weapon)));
std::cout << enchanted->GetName() << "\n"; // Sword of Fire of Poisonstd::cout << enchanted->GetDamage() << "\n"; // 33Composite 패턴
섹션 제목: “Composite 패턴”개별 객체와 객체 집합을 동일하게 처리합니다. 트리 구조(씬 그래프, UI 계층)에 적합합니다.
// 컴포넌트 인터페이스class SceneNode{public: virtual void Render() const = 0; virtual void AddChild(std::shared_ptr<SceneNode>) {} virtual ~SceneNode() = default;
std::string name; explicit SceneNode(std::string n) : name(std::move(n)) {}};
// Leaf: 자식 없음class MeshNode : public SceneNode{public: using SceneNode::SceneNode;
void Render() const override { std::cout << " Render mesh: " << name << "\n"; }};
// Composite: 자식 보유class GroupNode : public SceneNode{public: using SceneNode::SceneNode;
void AddChild(std::shared_ptr<SceneNode> child) override { _children.push_back(std::move(child)); }
void Render() const override { std::cout << "Group: " << name << "\n"; for (const auto& child : _children) child->Render(); }
private: std::vector<std::shared_ptr<SceneNode>> _children;};
// 씬 그래프 구성auto root = std::make_shared<GroupNode>("Root");auto body = std::make_shared<GroupNode>("Character");auto weapon = std::make_shared<MeshNode>("Sword");auto terrain = std::make_shared<MeshNode>("Terrain");
body->AddChild(std::make_shared<MeshNode>("Torso"));body->AddChild(std::make_shared<MeshNode>("Head"));body->AddChild(weapon);
root->AddChild(body);root->AddChild(terrain);
// 루트에서 Render 호출 → 전체 트리 순회root->Render();Facade 패턴
섹션 제목: “Facade 패턴”복잡한 서브시스템에 단순화된 인터페이스를 제공합니다.
// 복잡한 서브시스템class AudioSystem{public: void Initialize() { std::cout << "Audio init\n"; } void LoadBank(const std::string& bank) { std::cout << "Load bank: " << bank << "\n"; } void PlayEvent(const std::string& event) { std::cout << "Play: " << event << "\n"; }};
class PhysicsSystem{public: void Initialize(float gravity) { std::cout << "Physics init, gravity=" << gravity << "\n"; } void SetCollisionGroups(int groups) { }};
class RenderSystem{public: void Initialize(int width, int height) { std::cout << "Render " << width << "x" << height << "\n"; } void SetShadowQuality(int quality) { }};
// 파사드: 엔진 초기화를 단일 메서드로class GameEngineFacade{public: void Initialize() { _audio.Initialize(); _audio.LoadBank("Master"); _physics.Initialize(-9.8f); _physics.SetCollisionGroups(8); _render.Initialize(1920, 1080); _render.SetShadowQuality(2); }
void PlaySound(const std::string& event) { _audio.PlayEvent(event); }
private: AudioSystem _audio; PhysicsSystem _physics; RenderSystem _render;};
// 클라이언트: 복잡성을 모름GameEngineFacade engine;engine.Initialize();engine.PlaySound("explosion");Proxy 패턴
섹션 제목: “Proxy 패턴”객체에 대한 접근을 제어하는 대리자를 제공합니다. 지연 로딩, 캐싱, 접근 제어에 사용합니다.
class ITexture{public: virtual void Bind() = 0; virtual ~ITexture() = default;};
class Texture : public ITexture{public: explicit Texture(const std::string& path) { std::cout << "Loading texture: " << path << "\n"; // 실제 로딩 로직 } void Bind() override { std::cout << "Bind texture\n"; }};
// 지연 로딩 프록시class LazyTextureProxy : public ITexture{public: explicit LazyTextureProxy(std::string path) : _path(std::move(path)) {}
void Bind() override { // 실제 Bind 호출 시 처음 한 번만 로드 if (!_texture) _texture = std::make_unique<Texture>(_path);
_texture->Bind(); }
private: std::string _path; std::unique_ptr<Texture> _texture; // 필요할 때까지 null};
// 사용: 생성 시 로딩 없음auto tex = std::make_unique<LazyTextureProxy>("large_texture.png");std::cout << "Proxy created (not loaded yet)\n";
// 처음 Bind 호출 시 로딩 발생tex->Bind(); // "Loading texture: large_texture.png" + "Bind texture"tex->Bind(); // 두 번째 호출: 로딩 없이 바로 Bind| 패턴 | 의도 | 게임 활용 |
|---|---|---|
| Adapter | 인터페이스 변환 | 서드파티 물리/오디오 엔진 연결 |
| Decorator | 동적 기능 추가 | 무기 인챈트, 버프 스택 |
| Composite | 트리 구조 균일 처리 | 씬 그래프, UI 계층 |
| Facade | 복잡성 은닉 | 엔진 서브시스템 초기화 |
| Proxy | 접근 제어 | 지연 로딩, 캐싱 |
구조 패턴은 기존 코드를 수정하지 않고 새로운 기능을 조합할 수 있게 합니다. 특히 Decorator와 Composite는 게임 컴포넌트 시스템 설계에서 자주 만나는 패턴입니다.