Last active
July 22, 2022 15:34
-
-
Save jstaursky/9e5e6482bc02912fca3e1e1d6fcca8da to your computer and use it in GitHub Desktop.
_DESIGN_PATTERN_state
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
class Context; // fwd declare. User should only deal with the Context class. | |
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
class State | |
{ | |
public: | |
State (Context *c) : ctx (c) {} | |
virtual ~State() {}; | |
virtual void handle() = 0; | |
protected: | |
template <typename T> T* change_state(Context* context); | |
protected: | |
Context *ctx = nullptr; | |
}; | |
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
class ConcreteStateA : public State | |
{ | |
public: | |
using State::State; | |
ConcreteStateA (Context *c) : State (c), detail_a("state is A") | |
{ | |
std::cout << "A Ctor... " << std::endl; | |
} | |
void handle() override; | |
private: | |
std::string detail_a; | |
}; | |
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
class ConcreteStateB : public State | |
{ | |
public: | |
using State::State; | |
ConcreteStateB (Context *c) : State (c), detail_b ("state is B") | |
{ | |
std::cout << "B Ctor... " << std::endl; | |
} | |
void handle() override; | |
private: | |
std::string detail_b; | |
}; | |
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
class Context | |
{ | |
friend class State; | |
public: | |
Context() : _state (new ConcreteStateA (this)) | |
{} | |
~Context() { delete _state; } | |
void request() { _state->handle(); } | |
private: | |
State *_state; | |
}; | |
/* IMPLEMENTATIONS */ | |
template <typename T> T* State::change_state(Context* context) | |
{ | |
std::cout << "CHANGE STATE CALLED" << std::endl; | |
delete context->_state; | |
context->_state = new T (context); | |
return static_cast<T*>(context->_state); | |
} | |
auto ConcreteStateA::handle() -> void | |
{ | |
std::cout << "A State handle called." << std::endl; | |
auto updated_state = change_state<ConcreteStateB>(ctx); | |
// Need to return at this point. State is now ConcreteStateB. Accessing member variables | |
// that belong to ConcreteStateA at this point will cause crash (b/c 'change_state' | |
// called delete on 'CurrentState'). We are only still running inside ConcreteStateA at | |
// this point because code and data reside in different areas in memory. | |
updated_state->handle(); | |
return; | |
} | |
auto ConcreteStateB::handle() -> void | |
{ | |
std::cout << "B State handled called." << std::endl; | |
change_state<ConcreteStateA>(ctx); | |
// Need to return at this point. State is now ConcreteStateA. Accessing member variables | |
// that belong to ConcreteStateB at this point will cause crash (b/c 'change_state' | |
// called delete on 'CurrentState'). We are only still running inside ConcreteStateB at | |
// this point because code and data reside in different areas in memory. | |
return; | |
} | |
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
int main (int argc, char *argv[]) | |
{ | |
Context *context = new Context(); | |
context->request(); | |
delete context; | |
return 0; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <memory> | |
class Context; // fwd declare. | |
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
class State | |
{ | |
public: | |
State (Context *context) : _context (context) {} | |
virtual ~State() {}; | |
virtual void compute() = 0; | |
virtual void print_state_type () = 0; | |
protected: | |
Context *_context = nullptr; | |
}; | |
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
class A : public State | |
{ | |
public: | |
using State::State; | |
A (Context *context) : State (context), datum (5 + (rand() % 6)) {} | |
void compute() override; | |
void print_state_type () override { std::cout << "state A" << std::endl; } | |
void a_specific() { std::cout << "IM UNIQUE TO STATE A!\n"; } | |
private: | |
int datum; | |
}; | |
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
class B : public State | |
{ | |
public: | |
using State::State; | |
B (Context *context) : State (context), datum (2 + (rand() % 3)) {} | |
void compute() override; | |
void print_state_type () override { std::cout << "state B" << std::endl; } | |
void b_specific() { std::cout << "IM UNIQUE TO STATE B!\n"; } | |
private: | |
int datum; | |
}; | |
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
class Context | |
{ | |
public: | |
Context() : _state (new A (this)) | |
{} | |
void request() { _state->compute(); } | |
template <typename T> | |
T* change_state() | |
{ | |
std::cout << "CHANGE STATE CALLED" << std::endl; | |
_state = std::make_unique<T> (this); | |
return static_cast<T*>(_state.get()); | |
} | |
private: | |
std::unique_ptr<State> _state; | |
}; | |
/* IMPLEMENTATIONS */ | |
auto A::compute() -> void | |
{ | |
std::cout << "State is A... datum is " << (datum - 1) << std::endl; | |
if (--datum <= 0) { | |
auto* s = _context->change_state<B>(); | |
s->print_state_type (); | |
s->b_specific(); | |
} | |
return; | |
} | |
auto B::compute() -> void | |
{ | |
std::cout << "State is B... datum is " << (datum - 1) << std::endl; | |
if (--datum <= 0) { | |
auto* s = _context->change_state<A>(); | |
s->print_state_type(); | |
s->a_specific(); | |
} | |
return; | |
} | |
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
int main (int argc, char *argv[]) | |
{ | |
Context contoller; | |
for (int i = 1; i <= 50; ++i) { | |
contoller.request(); | |
} | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
class Player; // fwd declare. | |
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
class State | |
{ | |
public: | |
State (Player *owner) : Owner (owner) {} | |
virtual ~State() {}; | |
virtual void Step() = 0; | |
protected: | |
Player *Owner = nullptr; | |
}; | |
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
class StandState : public State | |
{ | |
public: | |
using State::State; | |
StandState (Player *owner) : State (owner), CountDown (5 + (rand() % 6)) | |
{ | |
std::cout << "STAND Ctor... countdown is " << CountDown << std::endl; | |
} | |
void Step() override; | |
private: | |
int CountDown; | |
}; | |
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
class JumpState : public State | |
{ | |
public: | |
using State::State; | |
JumpState (Player *owner) : State (owner), CountDown (2 + (rand() % 3)) | |
{ | |
std::cout << "JUMP Ctor... countdown is " << CountDown << std::endl; | |
} | |
void Step() override; | |
private: | |
int CountDown; | |
}; | |
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
class Player | |
{ | |
public: | |
Player() : CurrentState (new StandState (this)) | |
{} | |
void Step() | |
{ | |
CurrentState->Step(); | |
} | |
template <typename T> void ChangeCurrentState() | |
{ | |
std::cout << "CHANGE STATE CALLED" << std::endl; | |
delete CurrentState; | |
CurrentState = new T (this); | |
} | |
private: | |
State *CurrentState; | |
}; | |
/* IMPLEMENTATIONS */ | |
auto StandState::Step() -> void | |
{ | |
std::cout << "STAND State... countdown is " << (CountDown - 1) << std::endl; | |
if (--CountDown <= 0) | |
Owner->ChangeCurrentState<JumpState>(); | |
// Need to return at this point. State is now JumpState. Accessing member variables | |
// that belong to StandState at this point will cause crash (b/c 'ChangeCurrentState' | |
// called delete on 'CurrentState'). We are only still running inside StandState at | |
// this point because code and data reside in different areas in memory. | |
return; | |
} | |
auto JumpState::Step() -> void | |
{ | |
std::cout << "JUMP State... countdown is " << (CountDown - 1) << std::endl; | |
if (--CountDown <= 0) | |
Owner->ChangeCurrentState<StandState>(); | |
// Need to return at this point. State is now StandState. Accessing member variables | |
// that belong to JumpState at this point will cause crash (b/c 'ChangeCurrentState' | |
// called delete on 'CurrentState'). We are only still running inside JumpState at | |
// this point because code and data reside in different areas in memory. | |
return; | |
} | |
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
int main (int argc, char *argv[]) | |
{ | |
Player *player = new Player(); | |
for (int i = 1; i <= 50; ++i) { | |
player->Step(); | |
} | |
delete player; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment