강의는 C로 받았기에 AI에게 던져주고 C++로 전환했다.
파사드 패턴은 구조적 디자인 패턴 중 하나로, 복잡한 서브시스템들을 하나의 단순한 인터페이스로 감싸서 클라이언트가 쉽게 사용할 수 있게 함. C++에서는 보통 객체 구성을 사용해서 파사드 클래스가 서브시스템들의 인스턴스를 멤버로 가지고, 클라이언트의 요청을 받아 적절한 서브시스템에 위임하는 방식으로 구현한다.
게임에서 전투 시스템 : 공격, 데미지 계산, 애니메이션, 사운드 같은 여러 서브시스템이 얽혀 있는데, 파사드가 “전투 턴 시작”이라는 하나의 메서드로 이 모든 걸 처리해준다. 클라이언트는 내부가 어떻게 돌아가는지 신경 쓸 필요가 없다.
C++로 대규모 프로젝트를 하다 보면 클래스와 메서드가 많아져서 코드가 복잡해진다. 클라이언트가 모든 서브시스템을 직접 호출하면 코드가 지저분해지고, 나중에 서브시스템이 바뀌면 클라이언트 코드도 같이 수정해야 한다.
파사드를 쓰면 클라이언트는 파사드 하나만 알면 되고, 내부 서브시스템이 바뀌어도 파사드 안에서 처리하면 되니까 유지보수가 쉬워진다. 게다가 C++에선 메모리 관리가 중요하니까, 파사드가 서브시스템 객체들의 생명주기를 관리하는 역할도 할 수 있다.
C++에서는 인터페이스를 추상 클래스로 정의하고, 서브시스템 클래스들이 이를 상속받아서 구체적인 기능을 구현한다. 파사드 클래스는 서브시스템들의 포인터를 멤버로 가지고, 클라이언트 요청을 받아서 적절히 위임한다.
C++ 특성상 메모리 관리가 중요해서, 파사드의 소멸자에서 동적으로 할당된 서브시스템 객체들을 해제하는 것도 잊지 말아야함.
게임의 전투 시스템을 예제. 플레이어, 적, 데미지 계산, 애니메이션, 사운드 이렇게 다섯 개 서브시스템이 있다고 치고, 파사드로 이걸 묶어본다면.
#include <iostream>
class EnemySystem {
public:
int health;
EnemySystem(int h) : health(h) {}
int attack() {
int damage = rand() % 11 + 10; // 10~20
std::cout << "[EnemySystem] 적이 " << damage << " 데미지를 입힙니다.\n";
return damage;
}
void receiveDamage(int damage) {
health -= damage;
if (health < 0) health = 0;
std::cout << "[EnemySystem] 적의 체력이 " << damage << " 감소, 현재 체력: " << health << "\n";
}
};
#include <iostream>
class PlayerSystem {
public:
int health;
int mana;
PlayerSystem(int h, int m) : health(h), mana(m) {}
int attack() {
int damage = rand() % 11 + 15; // 15~25
std::cout << "[PlayerSystem] 플레이어가 " << damage << " 데미지를 입힙니다.\n";
return damage;
}
void receiveDamage(int damage) {
health -= damage;
if (health < 0) health = 0;
std::cout << "[PlayerSystem] 플레이어의 체력이 " << damage << " 감소, 현재 체력: " << health << "\n";
}
bool useMana(int amount) {
if (mana < amount) {
std::cout << "[PlayerSystem] 마나 부족!\n";
return false;
}
mana -= amount;
std::cout << "[PlayerSystem] 마나 " << amount << " 소모, 남은 마나: " << mana << "\n";
return true;
}
};
#include <iostream>
class DamageCalculator {
public:
int calculateDamage(int baseDamage, int modifier) {
int total = baseDamage + modifier;
std::cout << "[DamageCalculator] 기본 " << baseDamage << "에 " << modifier << "를 더해 " << total << " 데미지.\n";
return total;
}
};
#include <iostream>
#include <chrono>
#include <thread>
class AnimationSystem {
public:
void playAttackAnimation(const std::string& actor) {
std::cout << "[AnimationSystem] " << actor << "의 공격 애니메이션 재생...\n";
std::this_thread::sleep_for(std::chrono::milliseconds(300));
std::cout << "[AnimationSystem] " << actor << "의 공격 애니메이션 완료.\n";
}
};
#include <iostream>
#include <chrono>
#include <thread>
class SoundSystem {
public:
void playSoundEffect(const std::string& effectName) {
std::cout << "[SoundSystem] '" << effectName << "' 사운드 재생...\n";
std::this_thread::sleep_for(std::chrono::milliseconds(200));
std::cout << "[SoundSystem] '" << effectName << "' 사운드 재생 완료.\n";
}
};
#include "EnemySystem.h"
#include "PlayerSystem.h"
#include "DamageCalculator.h"
#include "AnimationSystem.h"
#include "SoundSystem.h"
class BattleFacade {
private:
EnemySystem* enemy;
PlayerSystem* player;
DamageCalculator* damageCalculator;
AnimationSystem* animationSystem;
SoundSystem* soundSystem;
public:
BattleFacade(EnemySystem* e, PlayerSystem* p)
: enemy(e), player(p),
damageCalculator(new DamageCalculator()),
animationSystem(new AnimationSystem()),
soundSystem(new SoundSystem()) {}
~BattleFacade() {
delete damageCalculator;
delete animationSystem;
delete soundSystem;
}
void executeBattleTurn() {
std::cout << "\n--- 전투 턴 시작 ---\n";
animationSystem->playAttackAnimation("플레이어");
soundSystem->playSoundEffect("플레이어 공격 효과음");
int playerDamage = damageCalculator->calculateDamage(player->attack(), 5);
enemy->receiveDamage(playerDamage);
if (enemy->health > 0) {
animationSystem->playAttackAnimation("적");
soundSystem->playSoundEffect("적 공격 효과음");
int enemyDamage = damageCalculator->calculateDamage(enemy->attack(), -3);
player->receiveDamage(enemyDamage);
} else {
std::cout << "[BattleFacade] 적이 이미 쓰러졌습니다.\n";
}
std::cout << "--- 전투 턴 종료 ---\n";
}
};
BattleFacade가 동적으로 할당한 서브시스템 객체들(damageCalculator, animationSystem, soundSystem)을 소멸자에서 해제함. 하지만 enemy와 player는 외부에서 주입받은 포인터라 여기서 해제하지 않는다.#include "BattleFacade.h"
#include <iostream>
#include <ctime>
int main() {
std::srand(std::time(0)); // 랜덤 시드 설정
EnemySystem* enemy = new EnemySystem(100);
PlayerSystem* player = new PlayerSystem(120, 50);
BattleFacade battleFacade(enemy, player);
while (enemy->health > 0 && player->health > 0) {
battleFacade.executeBattleTurn();
std::this_thread::sleep_for(std::chrono::seconds(1));
}
std::cout << "전투가 종료되었습니다.\n";
delete enemy;
delete player;
return 0;
}
BattleFacade의 executeBattleTurn()만 호출한다. 내부적으로 애니메이션, 사운드, 데미지 계산이 다 처리된다.파사드 패턴은 C++에서 복잡한 서브시스템을 단순한 인터페이스로 감싸줘서 코드의 복잡성을 줄이고 유지보수를 쉽게 해준다. 포인터와 메모리 관리를 신경 써야 하지만, 위 예제처럼 전투 시스템을 깔끔하게 통합할 수 있다. C++의 강력한 기능을 활용하면 더 큰 효과를 볼 수 있다.