개인공부를 목적으로 만든 블로그입니다 ! 사실과 다른점이 있다면 이야기해주시면 감사하겠습니다 .
상태패턴과 전략패턴을 좀 더 자연스럽게 사용하고 싶어서
C++ 코드를 작성하면서 문득 의문점이 들었다.
지금까지 설계한 구조는 다음과 같다.
하지만 여기에서 IPlayerState의 Context에서 Player의 스테이트를 바꿔주기 위해서는
Player를 인자로 받거나 ContextState내부(이하 NormalState)에 Player를 멤버변수로 둬서 바꿔주는게 일반적이였다.
그러면 아예 NormalState에서 플레이어의 정보를 모르게 할 수는 없을까?라는 생각이 들어서 시작하게 되었다.
비효율적일 수 있지만 , 고생을 해야 좋은 코드를 알 수 있다는 점에서 하루를 투자할 가치가 있었다.
내 생각은 다음과 같았다.
지금의 구조에서는 어느정도의 결합이 유지가 되어 있는 상황인데, 이렇게 되면 Player가 변경되면 State도 변경될 가능성이 존재한다고 생각했고, (이제와서 생각해보면 사실 있는지 의문이다. 인자로 받는거면 Player내부 메서드를 호출할텐데)
암튼! 결합도를 더 낮춰주고 싶었다. Player 내부의 State는 인터페이스를 가지고 있어 결합도가 낮지만
State내부에는 Player를 소유하고 있기 때문에 결합도가 높다.
따라서 Player와 State사이에 인터페이스를 다음과 같이 하나 두게 되었다.
1. State내부에서 Player의 멤버를 건들고 싶었다.
(기존의 프로젝트는 항상 Player에서 건드렸기 때문에, 이번에는 반대로 해보고 싶었다.)
다시말해 State에 따른 Player의 능력치를 State에게 책임을 위임하고 싶었다.
2.
이렇게 구상을 하고 나니 또 다른 문제가 생겼다.
만일 Player가 여럿이라면 어떻게 처리를 할 것인가?
IContext 를 상속받는 Player1 , Player2가 있다면 그 클래스들에게 State를 독립적으로 부여해야만 했다.
따라서 의존성 주입을 사용해보기로 했다.
의존성 주입의 하나인 외부에서 생성자에 인자를 전달하는 방법을 사용하였고, 다음과 같이 코드를 작성하였다.
#ifndef PLAYERSTATE_H
#define PLAYERSTATE_H
#include "IPlayerBattle.h"
#include "ContextInterface.h"
#include <memory>
class Player;
class PlayerState
{
public:
PlayerState(std::shared_ptr<ContextInterface> CharacterInterface)
: Interface(CharacterInterface) {}
virtual void HandleAttack() = 0;
virtual void SetState() = 0;
protected:
std::shared_ptr<ContextInterface> Interface;
};
이렇게 되면 외부에서 Player1과 Player2를 구분할 수 있을 것이다.
겪은 에러들 :
1. NormalState에서 SetState를 호출하면서
void NormalState::SetState()
{
Interface->SetState(std::make_unique<BerserkState>(Interface));
}
다음과 같이 Interface를 인자로 전달해야한다.
이유는 생성자의 호출 순서는 부모->자식 순이기 때문에
부모의 생성자에서 의존성 주입을 받고 있는 상황이기 때문에 Interface의 정보가 필요하다.
또, 우리가 결국 State를 생성해야하는 것은 NormalState클래스이기 때문에 생성자를 다음과 같이 만들어주었다.
NormalState(std::shared_ptr<ContextInterface> characterInterface)
: PlayerState(characterInterface), count(0) {}
부모의 생성자를 호출해 내부적으로 Interface를 보관하는 형식이다.
이렇게 코드를 작성하고 나니 또, 문제가 생겼다.
IContext를 사용하기 위해서는 Player객체를 인자로 넣어 State를 생성해야하는데,
IContext는 추상클래스이기 때문에, 인스턴스화를 시킬 수 없다.
그래서 Player를 인자로 받아서 해당 Interface를 State에 주입해야하는데,
std::shared_ptr <Player> tmp=std::make_shared<Player>(this);
이렇게 코드를 작성하게되면 문제가 생긴다.
왜냐하면 make_shared
CurrentState = std::make_unique<NormalState>(shared_from_this());