C++ 전략 패턴과 인터페이스

정완훈·2025년 2월 26일

C++로 구현한 전략 패턴: 총기 시스템 설계

전략 패턴은 특정 작업을 캡슐화하여 동적으로 교체할 수 있게 하는 설계 패턴이다. 이 예제에서는 총기 시스템을 통해 전략 패턴을 구현한다. 코드의 각 부분이 어떤 역할을 하는지 살펴보자.

행동 인터페이스 정의

총기의 발사와 장전 동작을 정의하는 인터페이스를 만든다. 이는 전략 패턴에서 '전략' 역할을 한다.

  • FireBehavior: 발사 동작을 정의한다. fire() 메서드를 가진다.
  • ReloadBehavior: 장전 동작을 정의한다. reload() 메서드를 가진다.
class FireBehavior {
public:
    virtual ~FireBehavior() = default;
    virtual void fire() = 0;
};

class ReloadBehavior {
public:
    virtual ~ReloadBehavior() = default;
    virtual void reload() = 0;
};

구체적인 행동 구현

인터페이스를 기반으로 실제 동작을 구현하는 클래스들을 작성한다. 이는 다양한 전략의 구체적인 구현이다.

  • StandardFire: 일반 발사를 구현한다. "Bang! 일반 발사."를 출력한다.
  • ShotgunFire: 산탄 발사를 구현한다. "Boom! 산탄 발사."를 출력한다.
  • NoFire: 발사 불가를 구현한다. "Click... 발사 불가."를 출력한다.
  • StandardReload: 일반 장전을 구현한다. "장전 완료: 탄창 교체."를 출력한다.
  • NoReload: 장전 불필요를 구현한다. "장전 불필요."를 출력한다.
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 클래스 정의

Gun 클래스는 추상 클래스로, 발사와 장전 전략을 포함한다. 이는 전략 패턴에서 '컨텍스트' 역할을 한다.

  • fireBehaviorreloadBehavior: 발사와 장전 전략을 가리키는 포인터다.
  • performFire()performReload(): 현재 설정된 전략의 fire()reload()를 호출한다.
  • setFireBehavior()setReloadBehavior(): 전략을 동적으로 교체한다.
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 클래스를 상속받아 실제 총기들을 구현한다. 각 총기는 생성자에서 적절한 전략을 설정한다.

  • Pistol: 권총이다. 일반 발사와 일반 장전을 사용한다.
  • Shotgun: 샷건이다. 산탄 발사와 일반 장전을 사용한다.
  • ToyGun: 장난감 총이다. 발사 불가와 장전 불필요를 사용한다.
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! 일반 발사."

0개의 댓글