よみがえるSingleton
もう少し引っ張るSingleton。
Modern C++ DesignにもC++におけるSingletonの実装の話が載っている。
GoF本におけるSingletonの説明を単純化すると「あるクラスのインスタンスがたった1つになることを保証し、それに対する大域的なアクセス・ポイントを提供する」ということになります。
Singletonはグローバル変数を改良したものです。
本書ではSingletonの寿命に関する実装に詳しく触れられており、
実際のSingletonの実装に「正解はない」としながらも、
実装上の様様な問題点を挙げて解説している。
インスタンスを破棄した後にも復活するPhoenix Singletonなんてものまで出てくるが、
要するにこれらは「Singletonが1つのインスタンスを保証するが故に生ずる問題点」である。
が、恐らく巷で求められているのは「大域的なアクセス・ポイント」の側面だろうという事が出来る。
例えばGamePadなるクラスがあった場合、これらのインスタンスは恐らく1つである事が分かる。
(複数のパッドに対応する為に複数のインスタンスがあるデザインでも構わないけれどもManager的なものは1つのインスタンスになるだろう)
で、これにアクセスする場合、
void App::mainLoop() { if (GamePad::GetInstance().getPad(0).isPushButton(GamePad::PAD_A)) { ... } }
という感じで書いてしまう事もできる。
でも、これを違った形で設計できないものだろうか?
Modern C++ Design―ジェネリック・プログラミングおよびデザイン・パターンを利用するための究極のテンプレート活用術 (C++ In‐Depth Series)
- 作者: アンドレイアレキサンドレスク,Andrei Alexandrescu,村上雅章
- 出版社/メーカー: ピアソンエデュケーション
- 発売日: 2001/12
- メディア: 単行本
- 購入: 9人 クリック: 194回
- この商品を含むブログ (99件) を見る
どうして大域的アクセスが無ければ困るのか?
Singletonの続き。:-[
要するに
GamePadを大域的アクセス可能なクラスとして扱いたい理由としては
class Character { public: Character(){} virtual ~Character(){} virtual void execute() = 0; }; class Player : public Character { public: Player(){}; virtual ~Player(){}; virtual void execute(){ } };
なんてクラスがある場合、
PlayerクラスではGamePadを扱いたい筈。
メニュなんかでもそうだけれど、
入力を受け取りたい箇所というのは幾つもある。
この場合、大域的アクセスにしてしまえば何も考えずに済む。
でも、ちょっと待った。
別にPlayerクラスがGamePadクラスをCompositionで良いんじゃない?
(DIでも良いケド)
要するに、
class Player : public Character { public: Player(){}; virtual ~Player(){}; virtual void execute(){ if (gamePad.isPushButton(GamePad::PAD_A)) { // ... } } private: GamePad gamePad; };
とする。
GamePadは複数のインスタンスとして存在するが
実際には複数のGamePadが1つのGamePadImplなインスタンスからデータを受け取って、
単なるインターフェイスとして機能すれば良い。
そんなことはないかしらん?
まだまだ続くよッ!Singleton
Singletonとんとん。
で、GamePadなら、
class GamePad { public: enum Button { PAD_A, PAD_B, }; private: class GamePadImpl { public: GamePadImpl(){}; virtual ~GamePadImpl(){}; bool isPushButton(Button b) { return true; } void update(){} }; static GamePadImpl gamePadImpl; public: static void update() { gamePadImpl.update(); } bool isPushButton( Button b ) { return gamePadImpl.isPushButton(b); } };
みたいな感じにする。
勿論、Repeat(繰り返し入力)やらTrigger(押された瞬間)やRelease(離された瞬間)などに対応するため、
必ず単一のインスタンスに対するアップデートメソッド、
GamePad::update();
をどこか(恐らくゲームループ中)で呼び出す必要はあるけれど、
これは得に問題がない筈。
グローバル変数「もどき」より良いと考えているけれど、どうだろうか。
勿論、GamePadImplはprivateなので
new GamePad::GamePadImpl();
なんてことはできない。
げーむぷろぐらまはよもう
programming: Tim Sweeny: A Programming Language for 2010 http://www.st.cs.uni-sb.de/edu/seminare/2005/advanced-fp/docs/sweeny.pdf
404 Not Found
いそぴーは目を通しておくべきだね :) (60ページ以上あるけど、プレゼンのスライドだからそんなにテキストの量はない)
C/C++なんて使ってるばやいじゃない!!!!
とはいえ現実は厳しく。
明らかに「同期」と「信頼性」は足りてないですけどねん。orz