本文へスキップ
BecomeCoder

Javaデザインパターンコース · 第2章 生成に関するパターン ― オブジェクトの「作り方」を工夫する · レッスン3

Singleton ― インスタンスをただ1つに保証する

ブラウザで完結

導入

アプリ全体でただ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 == l2new で2回作ったわけではないので、l1l2同じインスタンスtrue)になります。だから count も2つの呼び出しで共有され、[LOG 1] [LOG 2] と積み上がります。

やってみよう

下のエディタを実行して、l1 == l2true になること、そして2回の log 呼び出しで count が共有されていることを確かめましょう。Logger l3 = Logger.getInstance(); を追加して l3.log(...) を呼んでも、番号が続きで増えることを確認してください。

演習

AppSettings という Singleton クラスを作ってください。private int volume = 50; を持ち、getInstance() で唯一のインスタンスを返し、setVolume(int volume)getVolume() を持たせます。mainAppSettings 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 からも見えるのは、両方が同じインスタンスだからです。

実際に動かしてみよう

下のエディタにJavaを書いて「実行」を押すと、学習用シミュレータが System.out.println の出力を表示します(本物のJVMではなく、教材の範囲を再現した軽量エンジンです)。クラス・継承・コレクションなども動きます。Scanner で入力を読む例は「標準入力」欄に値を入れてください。本文の例を書き換えて動かしてみましょう(Stream・ラムダ・スレッド・ファイル入出力など一部の機能は対象外です)。

Java — ブラウザ内で実行(学習用シミュレータ)

Javaの教材サブセットを動かす学習用シミュレータを読み込みます(本物のJVMではなく、動きを再現した軽量な自作エンジンです)。
スクロールして表示された時点でも自動で読み込まれます。