Stateパターンを使ってみた
まずこのStateパターンを実装するにあたってQiitaの記事を参考にさせてもらった。
https://qiita.com/imadedede/items/180cf808eb74f5dda730
C++ で State パターンを書いてみたい – Qiita
ほとんどパクってます😅すみません。
まずは全体の構造としてクラス図で解説してくれているぺージがあるので、以下を参照。
https://qiita.com/i-tanaka730/items/49ee4e3daa3aeaf6e0b5
デザインパターン ~State~ – Qiita
私も感覚的に分かっているわけではないので、間違っている箇所もあると思います。
その点はご容赦下さい…(´・ω・`)
// State pattern context interface class.
class IPhaseContext {
public:
virtual ~IPhaseContext() {}
virtual bool recordLog(std::string title, std::string body) = 0;
virtual void setState(interface__::IPhaseState *objects) = 0;
};
まずは状態遷移を行うステートパターンのインターフェースです。
これを使ってゲームシーンとか場面の切り替えを行っていく。
class IPhaseContext; // This class exists in a separate file and needs to reference each other.
// State pattern's state interface class.
class IPhaseState {
public:
virtual ~IPhaseState() {}
virtual void doExecute(IPhaseContext* obj) = 0;
// doDrawSprites, doDrawBackgrounds, doPlaySoundEffects, doPlayBGMs, getInputInfo, doRecalculateRegister and more...
// Should be implemented virtual functions related to input, operation, and output.
};
次にステートパターンで切り替える各シーンの基底クラスです。
これもインターフェースです。
C++には interface キーワードがないので(なんかVisual Studioにはあったけど…💧)仮想関数を組み込んだクラスを書きます。
これはC++の記法ですね😀
そして次。
// Behavior management class for switching between multiple scenes.
// Extends MainComponent abstract class.
class PhaseComponent : abstract__::MainComponent, interface__::IPhaseContext {
private:
interface__::IPhaseState *pars_;
public:
PhaseComponent(int num = 0);
~PhaseComponent();
bool recordLog(std::string title, std::string body);
void setState(interface__::IPhaseState *objects);
bool doTrigger(void);
};
PhaseComponent::PhaseComponent(int num) : MainComponent(num) {
pars_ = new StandbyState();
}
PhaseComponent::~PhaseComponent() {
delete pars_;
pars_ = nullptr;
}
bool PhaseComponent::recordLog(std::string title, std::string body) {
using namespace forms::trace;
duplicate::FormalDuplicate::getInstance()->doWriteILogger(title, body);
return true;
}
void PhaseComponent::setState(interface__::IPhaseState *objects) {
delete pars_;
pars_ = objects;
}
bool PhaseComponent::doTrigger(void) {
if (nullptr != pars_) {
pars_->doExecute(this);
}
return true;
}
名前空間 「interface__」でIPhaseContextクラス、IPhaseStateクラスを使用します。
pars_のメンバ変数ポインタにIPhaseStateインターフェースを継承した具象クラスのインスタンスを突っ込みます。
シーン切り替え時にこのステートを捨てて(delete)、次の具象クラスをインスタンス化(new)します。
class StandbyState final : public interface__::IPhaseState {
public:
StandbyState();
void doExecute(interface__::IPhaseContext* obj);
};
StandbyState::StandbyState() {
SetBackgroundColor(0xFF, 0xFF, 0xFF);
}
void StandbyState::doExecute(interface__::IPhaseContext* obj) {
DrawString(1, 1, "STANDBY STATE CONNECTION NOW...", GetColor(0x80, 0x80, 0x80));
if (1 == C16Key::getInstance()->getC16PressButtons(GPAD_BUTTON::START_KEY)) {
MessageBox(GetMainWindowHandle(), "executed successfully of Eris standby process.", "State info", MB_OK);
obj->recordLog("Process-Log", "executed successfully of Eris standby process.");
obj->setState(new CreditLogoState());
}
}
中身のごちゃごちゃしたのは無視してもらって、
obj->setState(…)の部分でインスタンスを作成していますよね。
これが以下になります。
void PhaseComponent::setState(interface__::IPhaseState *objects) {
delete pars_;
pars_ = objects;
}
PhaseComponentクラスはIPhaseContextインターフェースを継承しているので、インスタンスの間接参照で、setStateメソッドが呼び出せます。
これでobjectsポインタが持っているインスタンスをpars_メンバに突っ込んでステートの引継ぎが完了します。
仕組みはこういったものです。
これを行う事で、長ったらしいswitch文を完全に削除できます。
コメント