導入
アプリ全体でただ1つだけ存在してほしいオブジェクトがあります。たとえば「アプリ全体の設定」や「ログの出力係」。あちこちで new Logger() されてしまうと、設定がバラバラになったり、ログの出力先が食い違ったりします。Singleton(シングルトン) パターンは、「インスタンスは絶対に1つだけ」をクラス自身に保証させる型です。
説明
Singletonの仕組みはシンプルです。コンストラクタを private にして外部から new させず、代わりに static なメソッド(多くは getInstance())経由でしか取得できないようにします。そのメソッドは、まだ作られていなければ作り、すでにあればそれを返します。
public class Main {
public static void main(String[] args) {
Logger l1 = Logger.getInstance();
Logger l2 = Logger.getInstance();
l1.log("起動しました");
l2.log("設定を読み込みました");
System.out.println(l1 == l2);
}
}
class Logger {
private static Logger instance;
private int count = 0;
private Logger() {
}
static Logger getInstance() {
if (instance == null) {
instance = new Logger();
}
return instance;
}
void log(String message) {
count++;
System.out.println("[LOG " + count + "] " + message);
}
}
classDiagram
class Logger {
-Logger instance$
-int count
-Logger()
+getInstance() Logger$
+log(message)
}
private static Logger instance;… クラス自身が「唯一のインスタンス」を保持するための場所。private Logger() { }… コンストラクタをprivateにすることで、クラスの外からnew Logger()できなくなります。static Logger getInstance()… 呼ばれるたびに「まだなければ作る、あれば返す」を行う唯一の入口です。l1 == l2…newで2回作ったわけではないので、l1とl2は同じインスタンス(true)になります。だからcountも2つの呼び出しで共有され、[LOG 1][LOG 2]と積み上がります。
やってみよう
下のエディタを実行して、l1 == l2 が true になること、そして2回の log 呼び出しで count が共有されていることを確かめましょう。Logger l3 = Logger.getInstance(); を追加して l3.log(...) を呼んでも、番号が続きで増えることを確認してください。
演習
AppSettings という Singleton クラスを作ってください。private int volume = 50; を持ち、getInstance() で唯一のインスタンスを返し、setVolume(int volume) と getVolume() を持たせます。main で AppSettings a = AppSettings.getInstance(); を作って a.setVolume(80); し、別の変数 AppSettings b = AppSettings.getInstance(); から b.getVolume() を表示して、80 になる(=同じインスタンスを指している)ことを確認してください。
ヒント1を見る
Logger をまねて private static AppSettings instance; と private AppSettings() { } を用意します。
ヒント2を見る
static AppSettings getInstance() { if (instance == null) { instance = new AppSettings(); } return instance; }。a で設定した値が b からも見えるのは、両方が同じインスタンスだからです。