// Purpose. State #include // // Discussion. The boss's behavior is class Boss { // "morphing" radically as a function public: // of his mood. Operations have large friend class Disposition; // "case" constructs that depend on Boss(); // this "state" attribute. Like large void decide(); // procedures, large conditional stmts void direct(); // are undesirable. They're monolith- private: // ic, and tend to make maintenance Disposition* moods_[2]; // very difficult. The State pattern int current_; // models individual states with de- }; // rived classes of an inheritance // hierarchy, and front-ends this class Disposition { // hierarchy with an "interface" public: // object that knows its "current" virtual void decide( Boss* ) = 0; // state. This partitions and local- virtual void direct( Boss* ) = 0; // izes all state-specific responsi- protected: // bilities; allowing for a cleaner void toggle( Boss* b ) { // implementation of dynamic behavior b->current_ = ! b->current_; } // that must change as internal state }; // changes. class DilbertZone : class Boss { public Disposition { public: public: void decide( Boss* b ) { Boss() { cout << "Eenie, meenie, mynie,"; mood_ = DilbertZone; cout << " moe.\n"; toggle(b); } } void direct( Boss* b ) { void decide() { cout << "My whim - you're"; if (mood_ == DilbertZone) { cout << " nightmare.\n"; cout << "Eenie, meenie,"; toggle(b); } cout << " mynie, moe.\n"; }; } class Sunny : else if (mood_ == Sunny) { public Disposition { public: cout << "You need it - you"; void decide( Boss* b ) { cout << " got it.\n"; cout << "You need it - you got"; } cout << " it.\n"; toggle(b); } toggle(); void direct( Boss* b ) { } cout << "Follow me.\n"; void direct() { toggle(b); } if (mood_ == DilbertZone) { }; cout << "My whim - you're"; cout << " nightmare.\n"; Boss::Boss() { } moods_[0] = new DilbertZone; else if (mood_ == Sunny) moods_[1] = new Sunny; cout << "Follow me.\n"; current_ = 0; } toggle(); void Boss::decide() { } moods_[current_]->decide( this ); } private: void Boss::direct() { enum Disposition { Sunny, moods_[current_]->direct( this ); } DilbertZone}; Disposition mood_; void main( void ) void toggle() { mood_ = ! mood_; } { }; Boss ph; for (int i=0; i < 2; i++) { void main( void ) ph.decide(); { ph.decide(); Boss ph; ph.direct(); } for (int i=0; i < 2; i++) } { // Eenie, meenie, mynie, moe. ph.decide(); // You need it - you got it. ph.decide(); // My whim - you're nightmare. ph.direct(); // You need it - you got it. } // Eenie, meenie, mynie, moe. } // Follow me. // Purpose. State design pattern - an FSM with two states and // two events (distributed transition logic - logic in the // derived state classes) #include \ Event on off using namespace std; State \ ------- ------- ON nothing OFF class Machine { OFF ON nothing class State* current; public: Machine(); void setCurrent( State* s ) { current = s; } void on(); void off(); }; class State { public: virtual void on( Machine* m ) { cout << " already ON\n"; } virtual void off( Machine* m ) { cout << " already OFF\n"; } }; void Machine::on() { current->on( this ); } void Machine::off() { current->off( this ); } class ON : public State { public: ON() { cout << " ON-ctor "; }; ~ON() { cout << " dtor-ON\n"; }; void off( Machine* m ); }; class OFF : public State { public: OFF() { cout << " OFF-ctor "; }; ~OFF() { cout << " dtor-OFF\n"; }; void on( Machine* m ) { cout << " going from OFF to ON"; m->setCurrent( new ON() ); delete this; } }; void ON::off( Machine* m ) { cout << " going from ON to OFF"; m->setCurrent( new OFF() ); delete this; } Machine::Machine() { current = new OFF(); cout << '\n'; } void main( void ) { void (Machine::*ptrs[])() = { Machine::off, Machine::on }; Machine fsm; int num; while (1) { cout << "Enter 0/1: "; cin >> num; (fsm.*ptrs[num])(); } } // OFF-ctor // Enter 0/1: 0 // already OFF // Enter 0/1: 1 // going from OFF to ON ON-ctor dtor-OFF // Enter 0/1: 1 // already ON // Enter 0/1: 0 // going from ON to OFF OFF-ctor dtor-ON // Enter 0/1: 1 // going from OFF to ON ON-ctor dtor-OFF // Enter 0/1: 0 // going from ON to OFF OFF-ctor dtor-ON // Enter 0/1: 0 // already OFF // Enter 0/1: // Purpose. State design pattern - an FSM with two states and two events // (distributed transition logic - logic in the derived state classes) // State\Event Suck up money Drive through // // RedLight you're a you're // victim, dead, // maybe change change to RED // to GREEN // // GreenLight you're an you're free // idiot but you're // still a victim, // change to RED #include #include using namespace std; class FSM { class State* current; public: FSM(); void setCurrent( State* s ) { current = s; } void suckUpMoney( int ); void carDrivesThrough(); }; class State { int total; protected: int getTotal() { return total; } public: State() { total = 0; } virtual void suckUpMoney( int in, FSM* fsm ) { total += in; cout << "total is " << total << '\n'; } virtual void carDrivesThrough( FSM* fsm ) = 0; }; class GreenLight : public State { public: GreenLight() { cout << "GREEN light\n"; } void suckUpMoney( int in, FSM* fsm ) { cout << " You're an idiot, "; State::suckUpMoney( in, fsm ); } void carDrivesThrough( FSM* fsm ); }; class RedLight : public State { public: RedLight() { cout << "RED light\n"; } void suckUpMoney( int in, FSM* fsm ) { cout << " "; State::suckUpMoney( in, fsm ); if (getTotal() >= 50) { fsm->setCurrent( new GreenLight ); delete this; } } void carDrivesThrough( FSM* fsm ) { cout << "Sirens!! Heat-seeking missile!! Confiscate net worth!!\n"; fsm->setCurrent( new RedLight ); delete this; } }; FSM::FSM() { current = new RedLight(); } void FSM::suckUpMoney( int in ) { current->suckUpMoney( in, this ); } void FSM::carDrivesThrough() { current->carDrivesThrough( this ); } void GreenLight::carDrivesThrough( FSM* fsm ) { cout << "Good-bye sucker!!\n"; fsm->setCurrent( new RedLight ); delete this; } int getCoin() { static int choices[3] = { 5, 10, 25 }; return choices[rand() % 3]; } void main( void ) { srand( time(0) ); FSM fsm; int ans; while (true) { cout << " Shell out (1), Drive thru (2), Exit (0): "; cin >> ans; if (ans == 1) fsm.suckUpMoney( getCoin() ); else if (ans == 2) fsm.carDrivesThrough(); else break; } } // Purpose. State demo (centralized transition logic - logic in the FSM) // // Discussion. Who defines the state transitions? The State pattern does not // specify which participant defines the criteria for state transitions. The // logic can be implemented entirely in the Context (FSM). It is generally // more flexible and appropriate, however, to let the State subclasses them- // selves specify their successor state and when to make the transition. This // requires adding an interface to the Context that lets State objects set the // Context's current state explicitly. A disadvantage of decentralization is // that State subclasses will be coupled to other sibling subclasses. [GOF308] #include class FSMstate { public: virtual void on() { cout << "undefined combo" << endl; } virtual void off() { cout << "undefined combo" << endl; } virtual void ack() { cout << "undefined combo" << endl; } }; class FSM { public: FSM(); void on() { states[current]->on(); current = next[current][0]; } void off() { states[current]->off(); current = next[current][1]; } void ack() { states[current]->ack(); current = next[current][2]; } private: FSMstate* states[3]; int current; int next[3][3]; }; class A : public FSMstate { public: void on() { cout << "A, on ==> A" << endl; } void off() { cout << "A, off ==> B" << endl; } void ack() { cout << "A, ack ==> C" << endl; } }; class B : public FSMstate { public: void off() { cout << "B, off ==> A" << endl; } void ack() { cout << "B, ack ==> C" << endl; } }; class C : public FSMstate { public: void ack() { cout << "C, ack ==> B" << endl; } }; FSM::FSM() { states[0] = new A; states[1] = new B; states[2] = new C; current = 1; next[0][0] = 0; next[0][1] = 1; next[0][2] = 2; next[1][0] = 1; next[1][1] = 0; next[1][2] = 2; next[2][0] = 2; next[2][1] = 2; next[2][2] = 1; } enum Message { On, Off, Ack }; Message messageArray[10] = { On,Off,Off,Ack,Ack,Ack,Ack,On,Off,Off }; void main( void ) { FSM fsm; for (int i = 0; i < 10; i++) { if (messageArray[i] == On) fsm.on(); else if (messageArray[i] == Off) fsm.off(); else if (messageArray[i] == Ack) fsm.ack(); } } // undefined combo // B, ack ==> C // B, off ==> A // C, ack ==> B // A, off ==> B // undefined combo // B, ack ==> C // B, off ==> A // C, ack ==> B // A, off ==> B