Singletonでコンストラクタに引数を使う必要がある場合に、シンプルに書く方法を探しています。
初期化とインスタンス取得のメソッドを分けて、初期化する可能性がある所から読む場合は必ず初期化を試してから取得、という方法しか思いついていません。
この方法だと呼び出し元が必ず初期化引数を持っているわけではないので呼び出し方が統一できず、今後破綻するように思われます。
TEST->initialize(param)
TEST->getInstance()
使用前に強制的に初期化できるようアプリケーションを変えてしまうのも手ですが、将来のアプリケーションの設計変更への準備を兼ねていることもあり難しい状況です。
よろしくお願い致します。
補足です。
1.初期化されていない時点で初期化不可能な場所から呼ばれるケースは(コードでは)現状無視するか例外で結構です。
2.一度初期化されている場合は変数が異なっていても追加の初期化指示は無視してかまいません。
3.> そもそもの設計がダメな気がしています。
回答しやすくなるかと思い書いた条件が裏目に出ているかもしれません。
質問の主旨は、引数つきで生成するインスタンスをシングルトンで扱いたい場合、なにかパターンのようなものがあるかどうか知りたい、というものでした。
特に、インスタンス取得と初期化を同じ入口で行う方法があるか、分けなければいけないとしても綺麗な実装方法はあるか、というのが主に知りたい部分です。
なので上の条件は一部無視してでも一般的な情報があればそれは助かります。(具体的な問題を解決するデザインパターンにこだわっているわけではありません)
質問が解りづらくて申し訳ありません。
よろしくお願い致します。
面白そうなので、ちょっと考えてみました。
こんな感じで、どうでしょう?
class TestSingleton { public: static TestSingleton* getInstance(const Parameter* param) { if (instance_ == not_initialized_) { if (param != NULL) { instance_ = new TestSingleton(param); } } return instance_; } virtual void method1() { ... } private: TestSingleton(const Parameter* param) { ... } TestSingleton() {} private: static TestSingleton* instance_; static TestSingleton* not_initialized_; // 初期化されていないインスタンス用のクラス class NotInitialized : public TestSingleton { // 全てのメソッドで、例外を送出する virtual void method1() { throw NotInitializedException(); } }; }; TestSingleton* TestSingleton::not_initialized_ = new TestSingleton::NotInitialized(); TestSingleton* TestSingleton::instance_ = not_initialized_;
同期とか、コピーコンストラクタとか、その辺りの Singleton のテンプレートは割愛してます。
スコープもちょっと怪しいけど、大目に見てください。
んで、使う側はこう。
// 初期化できる人 Parameter param = ... TestSingleton s = TestSingleton::getInstance(¶m); // 初期化できない人 TestSingleton s = TestSingleton::getInstance(NULL);
たたき台にでもなれば。
# 叩くときは、優しくお願いします :-)
面白そうなので、ちょっと考えてみました。
こんな感じで、どうでしょう?
class TestSingleton { public: static TestSingleton* getInstance(const Parameter* param) { if (instance_ == not_initialized_) { if (param != NULL) { instance_ = new TestSingleton(param); } } return instance_; } virtual void method1() { ... } private: TestSingleton(const Parameter* param) { ... } TestSingleton() {} private: static TestSingleton* instance_; static TestSingleton* not_initialized_; // 初期化されていないインスタンス用のクラス class NotInitialized : public TestSingleton { // 全てのメソッドで、例外を送出する virtual void method1() { throw NotInitializedException(); } }; }; TestSingleton* TestSingleton::not_initialized_ = new TestSingleton::NotInitialized(); TestSingleton* TestSingleton::instance_ = not_initialized_;
同期とか、コピーコンストラクタとか、その辺りの Singleton のテンプレートは割愛してます。
スコープもちょっと怪しいけど、大目に見てください。
んで、使う側はこう。
// 初期化できる人 Parameter param = ... TestSingleton s = TestSingleton::getInstance(¶m); // 初期化できない人 TestSingleton s = TestSingleton::getInstance(NULL);
たたき台にでもなれば。
# 叩くときは、優しくお願いします :-)
コメント(3件)
もし、最初の呼び出しが、呼び出し元が初期化用の引数を持ってないところからの Singleton の要求だった場合、どのように処理をするのでしょうか?
また、初期化用の引数を持っている呼び出し元からの、二回目以降の呼び出しで、初期化用の変数の値が違っている場合には、どのように動作するのが正しいのでしょうか?
Singleton の問題と言うよりは、処理の仕様の問題のように思えます。
上記のような、想定外のケースは、全て例外で落として良い、というのであれば、解決方法はあると思います。
Singletonを利用する目的は何ですか?
なので、ぼくがクラス設計をするときには、クラスが実体の写しであることを強く意識します。
で、Singleton が表すのは、その世界の中でひとつしかないもの。
なので、他からネタをもらわないと、自分が存在できない、ということに抵抗を感じます。
思ったのですが、質問で挙げられたようなインスタンスが、いくつもあるんじゃないでしょうか?
だとしたら、更新が一回だけに制限をかけた KEY-VALUE STORE みたいな map を作って、
その map が Singleton になっている、という手もあるかなあ。
でも、こういったことは実装だけの話で、そのインスタンスは、本当にあちこちから参照されるべき性格のものでしょうか?
メッセージの伝搬経路がきちんと設計されてなくて、後出しで、インスタンスを渡す必要が出てきたのだけれど、メソッドの引数を、あっちもこっちも直さなくちゃいけないじゃないか、みたいなことになってるんじゃないですか?
この一回は、大変な思いをして修正するにしても、次にこういうことがあったらどうするんだ、って。
プログラムが泥縄的に作られているから、クラス図を書いたとしたら、蜘蛛の巣のようになってるんじゃないでしょうか。
>そもそもの設計がダメな気がしています。
>Singletonを利用する目的は何ですか?
tdoi さんが書かれた、↑って、そういうことを指しているんじゃないかというような気がします。
もし、プロトタイプのようなものを作ってて、将来的に受け渡しをするデータが何になるか分からない、という話だったら、一連のメソッドで DTO を引数に持っておけば良かったね、というのが解決策じゃないかと思います。