State Pattern

Yongs12 ㅣ 2024. 2. 6. 19:31

 

상태 패턴 인터페이스 정의 (IChacterState)

#pragma once

/*================================
           State Pattern
================================*/

/*
상태 패턴은 객체가 가질 수 있는 상태를 추상화 하여 런타임에 유연하게 상태를
변경하고 필요한 경우 상태를 추가하기에도 용이한 패턴이다.
객체의 상태를 클래스로 표현한다.

FSM을 구현하기에 적합한 패턴이다.
FSM(finite-state machine) : 유한한 개수의 상태를 가질 수 있는 추상기계로 어떠한 사건에 의해
한 상태에서 다른 상태로 변화(전이)할 수 있다.

장점
ㄴ 유지보수 : 상태에 따른 행동이 분리 되어 있어 새로운 상태를 추가할 때 기존 코드를 수정하지 않아도 된다.
ㄴ 객체지향적 : 객체 지향 원리 중 단일 책임, 개방 폐쇄 원칙을 준수 할 수 있다.

단점
ㄴ 관리 : 상태 별로 클래스를 생성하므로 관리해야할 클래스 수 가 많아진다.
ㄴ 복잡성: 상태 간의 전환 로직이 복잡해질 수 있으며 각 상태의 관계를 명확히 이해해야 한다.

*/

/*
프로토타입 패턴으로 적용된 Character 클래스 재사용
*/
class Character;

class ICharacterState 
{
public:
    virtual ~ICharacterState() = default;

public:
    virtual void handleInput(Character* character, char input) = 0;
    virtual void update(Character* character) = 0;

};

 

 

상태 패턴을 상속하는 특정 상태 (Moving)

header file

#pragma once
#include "CharacterState.h"

class MovingState : public ICharacterState 
{
public:
    void handleInput(Character* character, char direction) override;
    void update(Character* character) override;

private:
    void move(Character* character, Vector3D position);
};

 

cpp file

#include "pch.h"
#include "MovingState.h"
#include "IdleState.h"
#include "Character.h"

void MovingState::handleInput(Character* character, char direction)
{
    Vector3D position = character->getPosition();
    const int speed = character->getSpeed();

    switch (direction)
    {
    case 'w':
        position.y += speed;
        break;
    case 's':
        position.y -= speed;
        break;
    case 'a':
        position.x -= speed;
        break;
    case 'd':
        position.x += speed;
        break;
    default:
        character->addState(make_unique<IdleState>());
        break;
    }
    move(character, position);
}


void MovingState::move(Character* character, Vector3D position)
{
    character->setPosition(position);
    cout << character->getName() << " " << "현재 위치 : " << position.x << ", " << position.y << position.z << "\n";
}

void MovingState::update(Character* character)
{
    cout << character->getName() << " : " << "움직이는 상태\n";
}

 

상태 패턴을 상속하는 특정 상태 (Idle)

header file

#pragma once
#include "CharacterState.h"

class IdleState : public ICharacterState
{
public:
    void handleInput(Character* character, char direction) override;
    void update(Character* character) override;
};

 

cpp file

#include "pch.h"
#include "IdleState.h"
#include "MovingState.h"
#include "Character.h"

void IdleState::handleInput(Character* character, char direction)
{
    if (direction == 'w' || direction == 'a' || direction == 's' || direction == 'd')
    {
        cout << "움직임 상태로 전환\n";
        character->setState(make_unique<MovingState>());
        character->getState()->handleInput(character, direction);
        return;
    }

    update(character);
}

void IdleState::update(Character* character)
{
    cout << character->getName() << " : " << "가만히 있는 상태\n";
}

 

 

Prototype으로 생성했던 Chracter 재사용

https://mygameprogramming.tistory.com/338

 

Prototype Pattern

Prototype 예시 (Character)#pragma once/*================================= Prototype Pattern=================================*//*프로토 타입 패턴은 객체 생성 방식 중 하나로 기존의 객체를 복제하여 새로운 객체를 만드는

mygameprogramming.tistory.com

 

header file

/* 추가 사항
Observer Pattern을 위한 IObserver 상속 추가
State Pattern을 위한 ICharacterState 멤버 변수 추가
*/
#include "Observer.h"
#include "CharacterState.h"
#include "IdleState.h"

class Character : public IObserver
{
public:
    Character(const Character&) = delete;
    Character& operator=(const Character&) = delete;
    virtual ~Character() {}

protected:
    Character() 
    {
        cout << "기본 아이들 상태\n";
        setState(make_unique<IdleState>());
    }
    Character(int hp, int mp, int speed, string name)
        : _hp(hp), _mp(mp), _speed(speed), _name(name)
    {
        cout << "기본 아이들 상태\n";
        setState(make_unique<IdleState>());
    }

public:
    int getHP() const 
    { 
        return _hp; 
    }
    int getMP() const 
    { 
        return _mp; 
    }
    int getSpeed() const 
    { 
        return _speed; 
    }

    string getName() const
    {
        return _name;
    }

    void setHP(int health) 
    { 
        _hp = health; 
    }
    void setMP(int magic) 
    { 
        _mp = magic; 
    }
    void setSpeed(int spd) 
    { 
        _speed = spd; 
    }

    void setName(const string& name)
    {
        _name = name;
    }

    void setPosition(const Vector3D& position)
    {
        _position = position;
    }

    Vector3D getPosition() const
    {
        return _position;
    }

    unique_ptr<ICharacterState>& getState()
    {
        return _currentState;
    }

public:
    virtual unique_ptr<Character> clone() const = 0;
    virtual void display() const = 0;
    virtual void attack() = 0;

/*공통 기능*/
public: 
    void update(EEventType event) override;
    void setState(unique_ptr<ICharacterState> newState);
    void stateUpdate(char input);

private:
    void dayUpdate();
    void nightUpdate();

private:
    string _name;
    int _hp = 0;    
    int _mp = 0;    
    int _speed = 0; 
    Vector3D _position;

    unique_ptr<ICharacterState> _currentState = make_unique<IdleState>();
};

 

 

cpp file

#include "pch.h"
#include "Character.h"

void Character::update(EEventType event) 
{
    switch (event) 
    {
    case EEventType::DAY:
        dayUpdate();
        break;
    case EEventType::NIGHT:
        nightUpdate();
        break;
    }
}

void Character::setState(unique_ptr<ICharacterState> newState) 
{
    cout << "새로운 상태 추가\n";
    _currentState = move(newState);
}

void Character::stateUpdate(char input) 
{
    if (_currentState != nullptr)
    {
        _currentState->handleInput(this, input);
    }
}

void Character::dayUpdate() 
{
    _speed += 5;

    if (_speed > 100) 
    {
        _speed = 100;
    }

    cout << _name << " 현재 이동 속도: " << _speed << " 낮일 때 이동 속도 증가\n";
}

void Character::nightUpdate() 
{
    _speed -= 10;

    if (_speed < 0) 
    {
        _speed = 0;
    }

    cout << _name << " 현재 이동 속도: " << _speed << " 밤일 때 이동 속도 감소\n";
}

 

 

상태 패턴 사용 (키 입력에 따른 상태 변화)

int main()
{
	Mage prototypeMage(100, 100, 80, "메이지");

	unique_ptr<Character> copyMage = prototypeMage.clone();

	bool run = true;

	while (run)
	{
		if (_kbhit())
		{
			char key = _getch();
			copyMage->stateUpdate(key);
		}
	}

	return 0;
}

 

실행 화면

'Design Pattern' 카테고리의 다른 글

Stategy pattern  (0) 2024.02.07
Observer Pattern  (0) 2024.02.05
Prototype Pattern  (0) 2024.02.04
Builder Pattern  (0) 2024.02.03
Factory Pattern  (0) 2024.02.02