読者です 読者をやめる 読者になる 読者になる

俺の報告

RoomClipを運営するエンジニアの日報(多分)です。

日報 #98 - デザインパターン:シングルトンについて

年の瀬でございます。
というか、クリスマスイブですか。
クリスマスと言えばGoFデザインパターンですよね。
23個のデザインパターンってほら、24と近いじゃないですか。
頃合いもよく知人から 「どうせ書くことねぇならデザインパターンを1個1個解説してくれよ」
というありがたい注文を頂いたので、ゆるやかーにおさらいしていきましょう。
出来るだけシンプルに、かつ誤解を与えない程度に実践的に。
解説の順番は「俺がよく使うやつ」順でいきます。
対象者は弊社社長くらいの、何となくJavaとかいじったことあるけど、
あんま詳しく分かんねぇっていう人向けのイメージで。

第一回目は「Singleton - シングルトン」です。

僕が今以上によく分かっていなかったとき、結構不思議に感じたのがシングルトンです。
シングルトンパターンってのは、早い話、該当クラスのインスタンスが絶対に1個しかないことを保証するパターンです。
1個しかインスタンスが作成されない、これにどれだけの有り難みがあるのか、その辺を明らかにしつつぼんやり解説していきます。

1個しかインスタンスを作成できなくさせること自体は結構簡単です。
すげー適当に言えばコンストラクタをprivateにしちゃえばいいんです。
new演算子でもなんでもいいんですが、クラスをインスタンスにする時は必ずコンストラクタという初期化を行います。
それを完全に隠蔽してしまえば、誰もインスタンスを作れなくなるわけです。
なので、こんな感じ。(Javaの時)

public class Hoge {
     private Hoge() {}
}

これで外部からnewとかできなくなったわけです。
とはいえ、一回はインスタンス化しないとだめです。 唯一自分をインスタンス化できるのは、自分だけなので、だから自分でやります。
やり方は普通にnew演算子なのですが、問題はタイミングです。
この辺は工夫のしがいがあるところでWikiとか見れば「安全な」タイミングについての議論が沢山あります。
ですが、ここでは一番分かりやすく、「任意のタイミングでプログラムがシングルトンを作れる」ようにしておきます。

その方法も簡単で、
外部から、シングルトンクラスに、自分自身をインスタンス化するように依頼すればいいんですね。
そういうメソッドを作っておけばいいわけです。
Hogeクラス内にHoge型でprivateな変数を宣言しておいて、
publicな「よろしく」メソッドHogeクラスの中でHogeクラスをインスタンス化する。
そいで必要な時はpublicな「くれ」メソッドHogeインスタンスを取得する。 こうしておけば、Hogeクラス内で自分のインスタンスを確保して、その出し入れを完全に管理できます。
とりあえず愚直に書いてみます。

public class Hoge {

     private static Hoge instance;

     private Hoge() {
          // なんもしない
     }

     // よろしくメソッドで、インスタンス化する
     public static Yoroshiku() {
            // すでにインスタンス化してたら何もしない←インスタンスが1個の保証ができている
            if(instance == null){
               instance = new Hoge();
            }
     }

     // くれメソッドが呼ばれた時だけ、自分のインスタンスを返す
     public static Hoge Kure() {
          return instance;
     }
}

よこせメソッドとよろしくメソッドがstaticになっているのに注意です。
Hogeクラスは「外部から絶対にインスタンス化されない」わけですので、
Hogeインスタンスが無い状態で、Hogeインスタンスメソッドを外部から実行する方法は皆無なのです。
なのでpublicでstaticなメソッドとしておきます。
そうすることで、クラスがロードされた時に実行できるメソッドとなれるわけですね。
(staticとかの静的領域のメモリ管理についてはまた別途。)

本当はinstanceメンバ変数もfinal staticとかのほうがいいんですが、
まぁ色々つくと分かりづらいのでこのくらい雑にしておきます。

ということで、
安全に1個のインスタンスを作ることに成功しました。
外部からなんどインスタンスをつくろうと、newしたりYoroshikuメソッドを叩いたりしても、
エラーになったり同じインスタンスが返答されたりするだけです。
(何度も言いますが、本当の本当はマルチスレッドなどを考慮してもっと厳密に1個である保証が必要です)

んで。
このパターンどこで使うのかと。

僕はシングルトンの重要な「基本的な」要素は3つだと思っています。

  1. インスタンスの数が1つで絶対保証されていること
  2. そのアクセス方法がグローバルであること
  3. そのインスタンスのあらゆるメソッドが呼び出し元のコンテキストに依存しないこと

1,2はまぁいいと思いますが、問題は3ですね。
これを要求ベースで書き直すとこうなります。

  1. そのインスタンスは1つしか必要ないし、それより多くても少なくてもだめ
  2. そのインスタンスにどっからでもアクセスできる
  3. どんな時、どんな場所でそのインスタンスメソッドを使ったって同じ動きをする

という感じです。
よく使われるのは「ログシステム」ですね。
ログシステムはゼロ個じゃ困りますし、2個あったら順番の制御やログ履歴の保持においても困ります。
だから絶対1個であるべきですね。
それに、ログシステムへのアクセスがあるコードからはできない、ってのは困ります。
どんなタイミングでも、どこでも出てほしいです。
また、ログの吐き方も、どんなタイミングでも同じになって欲しいです。
どっかの処理途中に「コンソールじゃなくてファイルにログを書き出す」みたいに設定しちゃった結果、 別の場所でその影響がでたら困ります。
なので、ログシステムは典型的なシングルトン適合タイプのシステムです。

ですが、僕は「デザイン」という観点からもシングルトンの意味について重要性があると思っています。
それはjsなどによくある話だと思いますが、、、
javascriptの場合、グローバルに使いたい変数や関数を、windowオブジェクトに直接ぶら下げるのはどうでしょうか。
それを許可するのはありでしょうか。
別にいい、という意見もあると思いますが、僕としては無条件にスコープがパブリックなwindowにぶら下げるのは安心できない手法だと思っています。
要は名前空間のようなものをjs上に実現しようとすると、結果シングルトンのようなものを作ることになることになると思っています。
ですので、プログラムの設計上シングルトンのようなものがあったほうが意味的に分かりやすいし使い易いというシーンも有るのだと考えています。
あくまで個人的ですが、、、

とても長くなってしまったので今日はここまで。
現場からは以上でした。