ブログトップ 記事一覧 ログイン 無料ブログ開設

神様なんて信じない僕らのために このページをアンテナに追加 RSSフィード

2006 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2012 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
基本的に適当な日記です。あまり鵜呑みにしないでください。土日はWeb離れしているので反応遅れます。

2007-12-27

[]Singletonを実現するgetInstanceが推奨できない理由

id:nicht-seinさんからコメントがあったので書いてみます。

まず、コメントにも書いてくださったようによく見かけるこれ

(僅かに手を加えました)

class CHoge {
private:
    CHoge() : value_(100) {};
    ~CHoge(){};
    int value_;
public:
    void setHoge(int value) { value_ = value; }
    int getHoge() const { return value_; }
    static CHoge& getInstance() {
        static CHoge instance_;
        return instance_;
    }
};

他からnewされないようにコンストラクタがprivateになってます。

勿論、スタックに置くためにCHoge hoge;もできません。

でも、実はこれはインスタンス化できます。

    printf("%d\n", CHoge::getInstance().getHoge() );
    CHoge* hoge = new CHoge(CHoge::getInstance());
    hoge->setHoge(999);
    printf("%d\n", hoge->getHoge() );
    printf("%d\n", CHoge::getInstance().getHoge() );

こんな感じ。

実行すると、インスタンスが二つあることがわかる。

なぜnewできるのかというと、暗黙のコピーコンストラクタがあるから。

(nicht-seinさんが挙げた例は単に省略していた可能性があります)

結局、これらを完全に防御する手段をするのは割と面倒なことです。

また、こうしたクラスを作っても、

似たような機能を持つSingletonでないクラスをつくられたら終わりです。

で、結論はそういうことではなくて、

結局のところ、

安易にgetInstanceのクラスを増やすと

「グローバル変数」を増やしているのと変わらないということ

それは要するに「密結合」であり、

「依存度の高さを不用意に高めている」ことに他ならない。

だいたい、

「本当にインスタンスが一つであることを完全に保証しなければならない」

ことってそもそもそんなに多発するケースなんだろうか?

そして、そのためにgetInstanceが溢れる世の中になっていいの?

if (Hoge::getInstance().getHoge() > 0) {
    if (Foo::getInstance().isFoo() && Bar::getInstance().isBar()) {
        ...
    }
}

極端な例だけれども、

こんなプログラムは見たくない。

if (Hoge().getHoge() > 0) {
    if (Foo().isFoo() && Bar().isBar()) {
        ...
    }
}

でも本質は一緒だ。

元々、Singletonは

「そのクラス自身を見たとき」

「そのインスタンスが一つしかない事を『表現』する」

ためにあるのであって、

他のクラスから見たとき、それが一つであるかどうかはどうでもいいし、

他のクラスからお手軽に(グローバル的に)アクセスすることを赦すために存在するわけじゃない。

そのインスタンスが単一のものであるかどうかなんてことは、

「そのクラス内で閉じているべき」なのだ。

ちょっと趣旨は違うけど

Singletonは、所詮、羊の皮を被ったグローバル変数であり、

相互依存や循環依存によって壊されることに注意しよう。

(初期化の依存関係について、C++ Coding Standardsより抜粋)

nicht-seinnicht-sein 2007/12/27 17:47 > 結局、これらを完全に防御する手段をするのは割と面倒なことです。
コピーコンストラクタと代入コンストラクタをprivateに宣言だけして実装を記述しなければ防げると思うのですが、それでもダメでしょうか?
コメントでは面倒でしたのでそのあたりは書きませんでした。

クラスの隠蔽性は高い方がよい、Singletonを多用するのがいけないというIsoparamerticさんの意見には賛成ですが、そのデメリットを理解した上で使うのなら問題は少ないと思います。

IsoparametricIsoparametric 2007/12/27 21:08 防ぐ、というのはコンパイルが通る通らない、というだけの意味ではありません。
文中にも示しましたが、それ(Singleton)と同様のコードを他で書かれてしまっては防ぐことはできません。

というか、
元々、デザインパターン(のSingleton)は「防ぐもの」ではなく「表現するもの」に過ぎません。
要するに防ぐためにSingletonにするわけではないのです。
単に防げる方が都合が良い(メリットがある)のでそういう感じに書くだけです。(副次的な効果です)


デメリットを理解した上で使うのは構わないと僕も思います。
ただ、
「グローバルに参照され、かつインスタンスは一つであることを表現しなければならないクラス」

なんていうものは殆ど存在しない、
と言うことができます。

書きたかったのは、そういうことです。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証