サーブレットのようなマルチスレッド環境下で共有リソースを扱うとき、複数のスレッド間で単一のオブジェクトを操作するようなプログラムが必要になることがあります。また、マルチスレッド環境以外でも、プログラム全体の情報を共有するオブジェクトや、生成に非常にコストが掛かるものの使い回しが利くオブジェクトのように、プログラム全体で1つのインスタンスだけを扱いたいこともあります。
小規模なプログラムなら、インスタンスを1つしか作らないという暗黙の規則を作り、注意深くコーディングすれば対応することはできるでしょう。しかし、プログラムの規模が大きくなってくると、そのような暗黙の規則だけではいずれ破たんしてしまいます。もともと1つだけのインスタンスを扱うように作られたクラスが、いつの間にか複数のインスタンスで扱われていたりすると、バグを引き起こす原因になります。
このような問題の解決には、クラスのstatic変数を利用して状態を共有するという方法があります。しかし、C/C++言語におけるグローバル変数に相当するstatic変数を多用してしまうと、クラス間の依存関係が強くなったり、動作を追跡するのが難しいコードにしてしまう原因となります。
このようなときは、Singletonパターンというデザインパターンを利用することをお勧めします。Singletonパターンを用いれば、構造上インスタンスを1つしか生成できないクラスを作成することができます。
package javatips; |
Singletonパターンのポイントは、「コンストラクタをprivateにしてしまう」ことです。そして、唯一のインスタンスをprivateなstatic変数として保持しておくのです。こうすることで、そのクラスがJava仮想マシンへロードされたときに、一度だけインスタンスが生成されます。そして、これ以後、インスタンスの生成は構造上不可能になります。インスタンスの取得には、専用のstaticメソッドを用意します。
すでにGang of Four(以下GoF)(注)によるオリジナルのSingletonパターンをご存じの方は、ここで示した実装が彼らの示した実装と少し異なっていることに気付かれたかもしれません。オリジナルに近いコードは、以下のようになります。しかし、こちらのコードは、マルチスレッド環境下でインスタンスが唯一であることを完全には保証できない、という問題が知られています。
注:Erich Gamma、Richard Helm、Ralph Johnson、John Vlissidesの4人を指します。デザインパターンをまとめた最初の書籍『オブジェクト指向における再利用のためのデザインパターン』の著者であり、彼らによって、このSingletonパターンを含む23個のデザインパターンが紹介されました。
public class GoFSingleton { |
こちらのコードの場合、getInstanceメソッドにsynchronized修飾子を付けることで、マルチスレッドの問題を回避することもできますが、メソッド呼び出しのたびに同期処理が行われるので、パフォーマンスが低下してしまいます。
また、この同期処理のパフォーマンス低下を避けるために、以下のような「二重チェック」(double-check)と呼ばれる手法を取ることも考えられますが、こちらはJava仮想マシンの実装によっては、正しく動作しない問題が指摘されています。
public static GoFSingleton getInstance() { |
従って、最初に示したMySingleton.javaの実装方法を採用するのが、最も無難です。
MySingletonクラスのインスタンスを取得するコードは、以下のようになります。ここでは、2つのインスタンスを取得して、Object#equals(Object)メソッドを用いて両者の同一性をチェックしています。
public static void main(String[] args) { |
このコードの実行結果は以下のようになります。
> javac javatips\MySingleton.java |
Singletonパターンは、デザインパターンの中では比較的簡単に理解でき、利用も容易です。そのため、多用される傾向にあるのですが、static変数の多用と同様で、Singletonパターンの多用も好ましくありません。グローバル変数ほどではないにせよ、クラス間の依存関係を不必要に強めることになるからです。
そのため、Singletonパターンの利用は、どうしてもインスタンスを1つに制限しなくてはならない場合だけに限定することをお勧めします。
Copyright © ITmedia, Inc. All Rights Reserved.