전략 패턴과 상태 패턴

정완훈·2025년 3월 12일

전략 패턴 적용: 무기 시스템을 유연하게

먼저 전략 패턴부터 살펴본다. 이건 객체의 행동을 런타임에 동적으로 바꾸고 싶을 때 쓰는 패턴이다. 예를 들어, 캐릭터가 들고 있는 무기에 따라 공격 방식이 달라져야 한다면 딱이다. 무기별로 공격력을 다르게 설정하고, 캐릭터가 그걸 자유롭게 바꿔가며 사용할 수 있게 해본다.

코드를 짜보면, 일단 무기 전략을 정의하는 인터페이스가 필요하다. 이름은 WeaponStrategy로 하고, 무기를 사용할 때의 행동을 추상 메서드로 만든다.

class WeaponStrategy {
public:
    virtual int useWeapon() const = 0;
    virtual ~WeaponStrategy() = default; // 가상 소멸자 추가, 메모리 문제 방지용
};

이제 각 무기마다 이 인터페이스를 구현한다. 롱소드는 공격력 10을 주고, 레이피어는 공격력 8에 추가로 치명타 확률 같은 걸 넣어볼까 싶었는데, 일단 단순하게 공격력만 다르게 해본다.

class LongSword : public WeaponStrategy {
public:
    int useWeapon() const override {
        return 10; // 롱소드는 공격력 10
    }
};

class Rapier : public WeaponStrategy {
public:
    int useWeapon() const override {
        return 8; // 레이피어는 공격력 8
    }
};

캐릭터 클래스에서는 현재 장착한 무기를 기반으로 공격력을 계산한다. 스마트 포인터(std::unique_ptr)를 써서 메모리 관리도 깔끔하게 해본다.

class Character {
private:
    std::unique_ptr<WeaponStrategy> weapon;

public:
    void equipWeapon(std::unique_ptr<WeaponStrategy> newWeapon) {
        weapon = std::move(newWeapon); // 무기 교체
    }

    int performAttack() {
        int baseDamage = 10; // 캐릭터 기본 공격력
        if (weapon) {
            baseDamage += weapon->useWeapon(); // 무기 공격력 추가
        }
        return baseDamage;
    }
};

이렇게 하면 캐릭터가 롱소드를 들면 공격력이 20이 되고, 레이피어로 바꾸면 18이 된다. 무기를 새로 추가하고 싶을 때도 WeaponStrategy를 구현하기만 하면 되니까 코드가 확장하기 쉬워진다. 꽤 깔끔하네.


상태 패턴 적용: 캐릭터 상태에 따른 변화

다음은 상태 패턴이다. 이건 객체의 내부 상태에 따라 행동을 바꾸고 싶을 때 유용하다. 캐릭터의 체력이 높을 때와 낮을 때 행동이 달라진다고 생각해보면 된다. 상태를 객체로 분리해서 관리하면 코드가 훨씬 깔끔해진다.

상태를 정의하는 인터페이스를 먼저 만든다. CharacterState라고 이름 짓고, 상태별로 공격력 보너스를 다르게 준다.

class CharacterState {
public:
    virtual int getAttackBonus() const = 0;
    virtual ~CharacterState() = default;
};

정상 상태와 저체력 상태를 구현해본다. 정상 상태에서는 공격력 변화가 없고, 저체력 상태에서는 공격력이 줄어들게 해본다.

class NormalState : public CharacterState {
public:
    int getAttackBonus() const override {
        return 0; // 정상 상태, 공격력 변화 없음
    }
};

class LowHealthState : public CharacterState {
public:
    int getAttackBonus() const override {
        return -5; // 저체력 상태, 공격력 5 감소
    }
};

캐릭터 클래스에 상태를 적용한다. 체력에 따라 상태를 바꾸는 로직도 추가해본다.

class Character {
private:
    std::unique_ptr<CharacterState> state;
    int health = 100;

public:
    void setState(std::unique_ptr<CharacterState> newState) {
        state = std::move(newState);
    }

    void takeDamage(int damage) {
        health -= damage;
        if (health < 30) {
            setState(std::make_unique<LowHealthState>());
        } else {
            setState(std::make_unique<NormalState>());
        }
    }

    int performAttack() {
        int baseDamage = 10; // 기본 공격력
        if (state) {
            baseDamage += state->getAttackBonus(); // 상태에 따른 보너스 적용
        }
        return baseDamage;
    }
};

이렇게 하면 체력이 30 아래로 떨어지면 공격력이 5 줄어든다. 상태가 바뀔 때마다 행동이 자연스럽게 변하니까, 새로운 상태를 추가할 때도 기존 코드를 건드릴 필요가 없다.


요점 정리: 전략 패턴 vs 상태 패턴

생각해보니 전략 패턴은 무기처럼 외부에서 교체 가능한 행동에 쓰고, 상태 패턴은 캐릭터의 내부 상태 변화에 맞춰서 쓰는구나. 둘 다 코드를 유연하게 만들어주는데, 적용하는 상황이 조금 다르다. 전략 패턴은 캐릭터가 무기를 바꿀 때마다 동작이 달라지고, 상태 패턴은 체력 같은 내부 조건에 따라 달라진다. 이렇게 패턴을 섞어서 쓰니까 유지보수도 쉬워지고, 새로운 기능 추가할 때 머리 아플 일도 줄어든다. 꽤 괜찮은 설계 방식인 것 같다.

0개의 댓글