State

  • 状態における振る舞いをそれぞれのクラスとして分割し、局所化する
  • 頻繁に状態が変わるような場合には、オブジェクトの生成処理のオーバーヘッドが大きくなる
    • 最初に全ての状態オブジェクトを生成して、オブジェクトの切り替えを行う

http://en.wikipedia.org/wiki/State_pattern
のJavaコードをC++にした
  1. #include <iostream>
  2. #include <string>
  3. #include <ctype.h>
  4. using namespace std;
  5.  
  6. // prototype
  7. class StateContext;
  8.  
  9. // State Class
  10. class State {
  11. public:
  12. virtual void writeName(StateContext *stateContext, string name) = 0;
  13. };
  14.  
  15. // ConcreteState Class
  16. class StateA : public State {
  17. public:
  18. virtual void writeName(StateContext *stateContext, string name);
  19. };
  20.  
  21. // ConcreteState Class
  22. class StateB : public State {
  23. public:
  24. StateB() {
  25. count = 0;
  26. }
  27. virtual void writeName(StateContext *stateContext, string name);
  28. private:
  29. int count;
  30. };
  31.  
  32. // Context Class
  33. class StateContext {
  34. public:
  35. StateContext() {
  36. myState = NULL;
  37. setState(new StateA());
  38. }
  39. void setState(State *newState) {
  40. if (myState != NULL) delete myState;
  41. myState = newState;
  42. }
  43. void writeName(const string &name) {
  44. myState->writeName(this, name);
  45. }
  46. private:
  47. State *myState;
  48. };
  49.  
  50. // StateA::writeName
  51. void StateA::writeName(StateContext *stateContext, string name) {
  52. string::iterator it = name.begin();
  53. while (it != name.end()) {
  54. *it = tolower(*it);
  55. ++it;
  56. }
  57. cout << name << endl;
  58. stateContext->setState(new StateB());
  59. }
  60.  
  61. // StateB::writeName
  62. void StateB::writeName(StateContext *stateContext, string name) {
  63. string::iterator it = name.begin();
  64. while (it != name.end()) {
  65. *it = toupper(*it);
  66. ++it;
  67. }
  68. cout << name << endl;
  69. if (++count > 1) {
  70. stateContext->setState(new StateA());
  71. }
  72. }
  73.  
  74. // client
  75. int main() {
  76. StateContext *sc = new StateContext();
  77. sc->writeName("Monday");
  78. sc->writeName("Tuesday");
  79. sc->writeName("Wednesday");
  80. sc->writeName("Thursday");
  81. sc->writeName("Saturday");
  82. sc->writeName("Sunday");
  83.  
  84. delete sc;
  85. return 0;
  86. }
  87.  
出力
monday
TUESDAY
WEDNESDAY
thursday
SATURDAY
SUNDAY

Structural example

  1. #include <iostream>
  2. #include <typeinfo>
  3. using namespace std;
  4.  
  5. // prototype
  6. class Context;
  7.  
  8. // The 'State' abstract class
  9. class State {
  10. public:
  11. virtual void Handle(Context *context) = 0;
  12. };
  13.  
  14. // The 'Context' class
  15. class Context {
  16. public:
  17. // constructor
  18. Context(State *state) {
  19. _state = NULL;
  20. SetState(state);
  21. }
  22. virtual ~Context() {
  23. delete _state;
  24. }
  25. // Get or Set the state
  26. State* GetState() {
  27. return _state;
  28. }
  29. void SetState(State *state) {
  30. if (_state != NULL) delete _state;
  31. _state = state;
  32. cout << "State: " << typeid(*state).name() << endl;
  33. }
  34. // Request
  35. void Request() {
  36. _state->Handle(this);
  37. }
  38. private:
  39. State *_state;
  40. };
  41.  
  42. // A 'ConcreteState' class
  43. class ConcreteStateA : public State {
  44. public:
  45. virtual void Handle(Context *context);
  46. };
  47.  
  48. // A 'ConcreteState' class
  49. class ConcreteStateB : public State {
  50. public:
  51. virtual void Handle(Context *context);
  52. };
  53.  
  54. // ConcreteStateA::Handle
  55. void ConcreteStateA::Handle(Context *context) {
  56. context->SetState(new ConcreteStateB());
  57. }
  58.  
  59. // ConcreteStateB::Handle
  60. void ConcreteStateB::Handle(Context *context) {
  61. context->SetState(new ConcreteStateA());
  62. }
  63.  
  64. // client
  65. int main() {
  66. // Setup context in a state
  67. Context *c = new Context(new ConcreteStateA());
  68.  
  69. // Issue requests, which toggles state
  70. c->Request();
  71. c->Request();
  72. c->Request();
  73. c->Request();
  74.  
  75. delete c;
  76. return 0;
  77. }
  78.  
出力(g++の場合)
State: 14ConcreteStateA
State: 14ConcreteStateB
State: 14ConcreteStateA
State: 14ConcreteStateB
State: 14ConcreteStateA

Real World example

  1. #include <iostream>
  2. #include <string>
  3. using namespace std;
  4.  
  5. class Account;
  6.  
  7. // The 'State' abstract class
  8. class State {
  9. protected:
  10. Account *account;
  11. double balance; // 残高
  12. double interest;
  13. double lowerLimit;
  14. double upperLimit;
  15. string state_name;
  16. public:
  17. // Properties
  18. Account* getAccount();
  19. void setAccount(Account *value);
  20. double getBalance() {
  21. return balance;
  22. }
  23. void setBalance(double value) {
  24. balance = value;
  25. }
  26. virtual void Deposit(double amount) = 0;
  27. virtual void Withdraw(double amount) = 0;
  28. virtual void PayInterest() = 0;
  29. string GetName() {
  30. return state_name;
  31. }
  32. };
  33.  
  34.  
  35. // The 'Context' class
  36. class Account {
  37. private:
  38. State *_state;
  39. string _owner;
  40. public:
  41. // Constructor
  42. Account(const string &owner);
  43. // Destructor
  44. virtual ~Account() {
  45. delete _state;
  46. }
  47. // Properties
  48. double getBalance() {
  49. return _state->getBalance();
  50. }
  51. State* getState() {
  52. return _state;
  53. }
  54. void setState(State *state) {
  55. delete _state;
  56. _state = state;
  57. }
  58. void Deposit(double amount) {
  59. _state->Deposit(amount);
  60. cout << "Deposited $" << amount << " ---" << endl;
  61. cout << " Balance = $" << getBalance() << endl;
  62. cout << " Status = " << getState()->GetName() << endl;
  63. cout << endl;
  64. }
  65. void Withdraw(double amount) {
  66. _state->Withdraw(amount);
  67. cout << "Withdraw $" << amount << " ---" << endl;
  68. cout << " Balance = $" << getBalance() << endl;
  69. cout << " Status = " << getState()->GetName() << endl;
  70. cout << endl;
  71. }
  72. void PayInterest() {
  73. _state->PayInterest();
  74. cout << "Interest Paid ---" << endl;
  75. cout << " Balance = $" << getBalance() << endl;
  76. cout << " Status = " << getState()->GetName() << endl;
  77. cout << endl;
  78. }
  79. };
  80.  
  81.  
  82.  
  83. // A 'ConcreteState' class
  84. // Red indicates that account is overdrawn
  85. class RedState : public State {
  86. private:
  87. double _serviceFee;
  88. public:
  89. // Constructor
  90. RedState(State *state) {
  91. balance = state->getBalance();
  92. account = state->getAccount();
  93. Initialize();
  94. }
  95. private:
  96. void Initialize() {
  97. // Should come from a datasource
  98. interest = 0.0;
  99. lowerLimit = -100.0;
  100. upperLimit = 0.0;
  101. _serviceFee = 15.00;
  102. state_name = "RedState";
  103. }
  104. public:
  105. virtual void Deposit(double amount) {
  106. balance += amount;
  107. StateChangeCheck();
  108. }
  109. virtual void Withdraw(double amount) {
  110. amount -= _serviceFee;
  111. cout << "No funds available for withdrawal!" << endl;
  112. }
  113. virtual void PayInterest() {
  114. // No interest is paid
  115. }
  116. private:
  117. void StateChangeCheck();
  118. };
  119.  
  120. // A 'ConcreteState' class
  121. // Silver indicates a non-interest bearing state
  122. class SilverState : public State {
  123. public:
  124. // Overloaded constructors
  125. SilverState(State *state) {
  126. balance = state->getBalance();
  127. account = state->getAccount();
  128. Initialize();
  129. }
  130. SilverState(double balance, Account *account) {
  131. this->balance = balance;
  132. this->account = account;
  133. Initialize();
  134. }
  135. private:
  136. void Initialize() {
  137. // Should come from a datasource
  138. interest = 0.0;
  139. lowerLimit = 0.0;
  140. upperLimit = 1000.0;
  141. state_name = "SilverState";
  142. }
  143. public:
  144. virtual void Deposit(double amount) {
  145. balance += amount;
  146. StateChangeCheck();
  147. }
  148. virtual void Withdraw(double amount) {
  149. balance -= amount;
  150. StateChangeCheck();
  151. }
  152. virtual void PayInterest() {
  153. balance += interest * balance;
  154. StateChangeCheck();
  155. }
  156. private:
  157. void StateChangeCheck();
  158. };
  159.  
  160.  
  161. // A 'ConcreteState' class
  162. // Gold indicates an interest bearing state
  163. class GoldState : public State {
  164. public:
  165. // Overloaded constructors
  166. GoldState(State *state) {
  167. balance = state->getBalance();
  168. account = state->getAccount();
  169. Initialize();
  170. }
  171. GoldState(double balance, Account *account) {
  172. this->balance = balance;
  173. this->account = account;
  174. Initialize();
  175. }
  176. private:
  177. void Initialize() {
  178. // Should come from a database
  179. interest = 0.05;
  180. lowerLimit = 1000.0;
  181. upperLimit = 10000000.0;
  182. state_name = "GoldState";
  183. }
  184. public:
  185. virtual void Deposit(double amount) {
  186. balance += amount;
  187. StateChangeCheck();
  188. }
  189. virtual void Withdraw(double amount) {
  190. balance -= amount;
  191. StateChangeCheck();
  192. }
  193. void PayInterest() {
  194. balance += interest * balance;
  195. StateChangeCheck();
  196. }
  197. private:
  198. void StateChangeCheck();
  199. };
  200.  
  201. //// State ////
  202. Account* State::getAccount() {
  203. return account;
  204. }
  205.  
  206. void State::setAccount(Account *value) {
  207. account = value;
  208. }
  209.  
  210. //// Account ////
  211. Account::Account(const string &owner) {
  212. // New accounts are 'Silver' by default
  213. _owner = owner;
  214. _state = new SilverState(0.0, this);
  215. }
  216.  
  217. //// RedState ////
  218. void RedState::StateChangeCheck() {
  219. if (balance > upperLimit) {
  220. account->setState(new SilverState(this));
  221. }
  222. }
  223.  
  224. //// SilverState ////
  225. void SilverState::StateChangeCheck() {
  226. if (balance < lowerLimit) {
  227. account->setState(new RedState(this));
  228. } else if (balance > upperLimit) {
  229. account->setState(new GoldState(this));
  230. }
  231. }
  232.  
  233. //// GoldState ////
  234. void GoldState::StateChangeCheck() {
  235. if (balance < 0.0) {
  236. account->setState(new RedState(this));
  237. } else if (balance < lowerLimit) {
  238. account->setState(new SilverState(this));
  239. }
  240. }
  241.  
  242.  
  243. ///////////
  244. // Entry point into console application.
  245. int main() {
  246. // Open a new account
  247. Account *account = new Account("Jim Johnson");
  248.  
  249. // Apply financial transactions
  250. account->Deposit(500.0); // deposit: 預け入れ
  251. account->Deposit(300.0);
  252. account->Deposit(550.0);
  253. account->PayInterest(); // interest: 利子
  254. account->Withdraw(2000.00); // withdraw: 引き落とし
  255. account->Withdraw(1100.00);
  256.  
  257. delete account;
  258. return 0;
  259. }
  260.  
出力
Deposited $500 ---
 Balance = $500
 Status = SilverState

Deposited $300 ---
 Balance = $800
 Status = SilverState

Deposited $550 ---
 Balance = $1350
 Status = GoldState

Interest Paid ---
 Balance = $1417.5
 Status = GoldState

Withdraw $2000 ---
 Balance = $-582.5
 Status = RedState

No funds available for withdrawal!
Withdraw $1100 ---
 Balance = $-582.5
 Status = RedState


参考サイト

デザインパターンを“喩え話”で分かり易く理解する
http://www.netlaputa.ne.jp/~hijk/study/oo/designpattern.html



デザインパターンの骸骨たち
http://www002.upp.so-net.ne.jp/ys_oota/mdp/

デザインパターンの使い方: State
http://japan.internet.com/developer/20081224/26.html
最終更新:2012年03月25日 12:07