C++로 구현한 전략 패턴: 총기 시스템 설계
전략 패턴은 특정 작업을 캡슐화하여 동적으로 교체할 수 있게 하는 설계 패턴이다. 이 예제에서는 총기 시스템을 통해 전략 패턴을 구현한다. 코드의 각 부분이 어떤 역할을 하는지 살펴보자.
총기의 발사와 장전 동작을 정의하는 인터페이스를 만든다. 이는 전략 패턴에서 '전략' 역할을 한다.
fire() 메서드를 가진다.reload() 메서드를 가진다.class FireBehavior {
public:
virtual ~FireBehavior() = default;
virtual void fire() = 0;
};
class ReloadBehavior {
public:
virtual ~ReloadBehavior() = default;
virtual void reload() = 0;
};
인터페이스를 기반으로 실제 동작을 구현하는 클래스들을 작성한다. 이는 다양한 전략의 구체적인 구현이다.
class StandardFire : public FireBehavior {
public:
void fire() override {
std::cout << "Bang! 일반 발사.\n";
}
};
class ShotgunFire : public FireBehavior {
public:
void fire() override {
std::cout << "Boom! 산탄 발사.\n";
}
};
class NoFire : public FireBehavior {
public:
void fire() override {
std::cout << "Click... 발사 불가.\n";
}
};
class StandardReload : public ReloadBehavior {
public:
void reload() override {
std::cout << "장전 완료: 탄창 교체.\n";
}
};
class NoReload : public ReloadBehavior {
public:
void reload() override {
std::cout << "장전 불필요.\n";
}
};
Gun 클래스는 추상 클래스로, 발사와 장전 전략을 포함한다. 이는 전략 패턴에서 '컨텍스트' 역할을 한다.
fire()와 reload()를 호출한다.class Gun {
protected:
FireBehavior* fireBehavior;
ReloadBehavior* reloadBehavior;
public:
Gun(FireBehavior* fb, ReloadBehavior* rb) : fireBehavior(fb), reloadBehavior(rb) {}
virtual ~Gun() {
delete fireBehavior;
delete reloadBehavior;
}
virtual void display() = 0;
void performFire() {
fireBehavior->fire();
}
void performReload() {
reloadBehavior->reload();
}
void setFireBehavior(FireBehavior* fb) {
delete fireBehavior;
fireBehavior = fb;
}
void setReloadBehavior(ReloadBehavior* rb) {
delete reloadBehavior;
reloadBehavior = rb;
}
};
Gun 클래스를 상속받아 실제 총기들을 구현한다. 각 총기는 생성자에서 적절한 전략을 설정한다.
class Pistol : public Gun {
public:
Pistol() : Gun(new StandardFire(), new StandardReload()) {}
void display() override {
std::cout << "권총\n";
}
};
class Shotgun : public Gun {
public:
Shotgun() : Gun(new ShotgunFire(), new StandardReload()) {}
void display() override {
std::cout << "샷건\n";
}
};
class ToyGun : public Gun {
public:
ToyGun() : Gun(new NoFire(), new NoReload()) {}
void display() override {
std::cout << "장난감 총\n";
}
};
메인 함수에서는 다양한 총기를 생성하고 동작을 테스트한다. 전략 교체의 유연성을 확인하기 위해 장난감 총의 발사 전략을 변경한다.
int main() {
Gun* pistol = new Pistol();
Gun* shotgun = new Shotgun();
Gun* toyGun = new ToyGun();
std::cout << "=== 권총 테스트 ===\n";
pistol->display();
pistol->performFire();
pistol->performReload();
std::cout << "=== 샷건 테스트 ===\n";
shotgun->display();
shotgun->performFire();
shotgun->performReload();
std::cout << "=== 장난감 총 테스트 ===\n";
toyGun->display();
toyGun->performFire(); // 기본: 발사 불가
toyGun->performReload(); // 기본: 장전 불필요
std::cout << "\n";
delete pistol;
delete shotgun;
delete toyGun;
return 0;
}
=== 권총 테스트 ===
권총
Bang! 일반 발사.
장전 완료: 탄창 교체.
=== 샷건 테스트 ===
샷건
Boom! 산탄 발사.
장전 완료: 탄창 교체.
=== 장난감 총 테스트 ===
장난감 총
Click... 발사 불가.
장전 불필요.
이 코드는 전략 패턴의 핵심을 보여준다. 행동을 인터페이스로 정의하고, 구체적인 구현을 별도로 분리하며, Gun 클래스가 이를 동적으로 사용한다. 각 부분이 역할을 명확히 나누어 코드의 구조를 깔끔하게 유지한다.
이제 장난감 총에도 실탄 장전을 넣는 기행도 가능하다.
접기/펼치기 int main() {
Gun* pistol = new Pistol();
Gun* shotgun = new Shotgun();
Gun* toyGun = new ToyGun();
std::cout << "=== 권총 테스트 ===\n";
pistol->display();
pistol->performFire();
pistol->performReload();
std::cout << "=== 샷건 테스트 ===\n";
shotgun->display();
shotgun->performFire();
shotgun->performReload();
std::cout << "=== 장난감 총 테스트 ===\n";
toyGun->display();
toyGun->performFire();
toyGun->performReload();
std::cout << "장난감 총에 실탄 발사 장착!\n";
toyGun->setFireBehavior(new StandardFire());
toyGun->performFire();
delete pistol;
delete shotgun;
delete toyGun;
return 0;
}
=== 권총 테스트 ===
권총
Bang! 일반 발사.
장전 완료: 탄창 교체.
=== 샷건 테스트 ===
샷건
Boom! 산탄 발사.
장전 완료: 탄창 교체.
=== 장난감 총 테스트 ===
장난감 총
Click... 발사 불가.
장전 불필요.
장난감 총에 실탄 발사 장착!
Bang! 일반 발사."