daisukeの技術ブログ

AI、機械学習、最適化、Pythonなどについて、技術調査、技術書の理解した内容、ソフトウェア/ツール作成について書いていきます

Javaでデザインパターンを学ぶ:Singletonパターン

Javaによるデザインパターンの1回目です!

教材は、参考文献の「Java言語で学ぶデザインパターン入門第3版」を使わせて頂きます。

今回は、Singletonパターンです。23個のデザインパターンの中でも、最も理解しやすいパターンの一つだと思います。

では、やっていきましょう!

参考文献

参考文献のサンプルプログラムのダウンロード

www.hyuki.com

はじめに

「Javaでデザインパターンを学ぶ」の記事一覧です。良かったら参考にしてください。

Javaでデザインパターンの記事一覧

先に、23種類のデザインパターンを示します。

参考サイト:デザインパターン (ソフトウェア) - Wikipedia

デザインパターン一覧
◆生成に関するパターン
 ・Abstract Factory:関連するインスタンスを状況に応じて、適切に生成する方法を提供する
 ・Builder:複合化されたインスタンスの生成過程を隠蔽する。
 ・Factory Method:生成されるインスタンスに依存しない、インスタンスの生成方法を提供する
 ・Prototype:同様のインスタンスを生成するために、原型のインスタンスを複製する
 ・Singleton:あるクラスについて、インスタンスが1つしか存在しないことを保証する
◆構造に関するパターン
 ・Adapter:元々関連性のない2つのクラスを接続するクラスを作る
 ・Bridge:クラスと呼び出し側の間の橋渡しをするクラスで、実装を隠蔽する
 ・Composite:再帰的な構造を表現する
 ・Decorator:あるインスタンスに対し、動的に付加機能を追加する
 ・Facade:複数のサブシステムの窓口となる共通のインタフェースを提供する
 ・Flyweight:多数のインスタンスを共有し、インスタンスの構築のための負荷を減らす
 ・Proxy:共通のインタフェースを持つインスタンスを内包し、利用者からのアクセスを代理する。Wrapperとも呼ばれる
◆振る舞いに関するパターン
 ・Chain of Responsibility:イベントの送受信を行う複数のオブジェクトを鎖状につなぎ、それらの間をイベントが渡されていくようにする
 ・Command:複数の異なる操作について、それぞれに対応するオブジェクトを用意し、オブジェクトを切り替えることで、操作の切り替えを実現する
 ・Interpreter:構文解析のために、文法規則を反映するクラス構造を作る
 ・Iterator:複数の要素を内包するオブジェクトのすべての要素に対して、順番にアクセスする方法を提供する
 ・Mediator:オブジェクト間の相互作用を仲介するオブジェクトを定義し、オブジェクト間の結合度を低くする
 ・Memento:データ構造に対する一連の操作のそれぞれを記録しておき、以前の状態の復帰または操作の再現が行えるようにする
 ・Observer:インスタンスの変化を他のインスタンスから監視できるようにする
 ・State:オブジェクトの状態を変化させることで、処理内容を変えられるようにする
 ・Strategy:データ構造に対して適用する一連のアルゴリズムをカプセル化し、アルゴリズムの切り替えを容易にする
 ・Template Method:アルゴリズムは抽象クラスで、処理内容はサブクラスで定義する
 ・Visitor:データ構造を保持するクラスと、それに対して処理を行うクラスを分離する

今回は、「Singleton」をやっていきます!

サンプルコードの理解

シングルトンは、対象クラスのインスタンスが必ず1つだけになることを保証できるデザインパターンです。

用途として、いろいろ調べてみてみたところ、実用的な例としては、以下のようなものでした。

  • ロガークラス
    • どこから使ってもログの設定は一緒であってほしい
    • すぐ使いたい(static変数は実行開始時に生成される)
  • 管理クラス
    • ハードウェア資源(メモリ、ソケットなど物理的に制御対象が固定されている)
    • 対象の資源の状態などは、複数あったら困る

確かに、シングルトンパターンを使うと、都合が良さそうですね。

Mainクラス(シングルトンを使う側のクラス)

まずは、シングルトンのクラスがどう使われるのかを見るために、Mainクラスを見ていきます。

public class Main {
    public static void main(String[] args) {
        System.out.println("Start.");
        for (int i = 0; i < 10; i++) {
            System.out.println(i + ":" + TicketMaker.getInstance().getNextTicketNumber());
        }
        System.out.println("End.");
    }
}

シングルトンのクラスは、TicketMaker です。

チケットメーカー(チケットを発行する機能を持つもの)に対して、getNextTicketNumber() (チケット番号を付与する)をコールすると、ユニークな(唯一の)チケット番号を発行してくれる、という感じでしょうか。

今回は、forループで、チケット番号を10回発行しています。

もし、普通のクラスで、複数のインスタンスが存在した場合、同じチケット番号が発行されると困りますね。シングルトンを使うと、そういう被りを防ぐために都合が良さそうです。

TicketMakerクラス

次は、シングルトンクラスの TicketMaker です。

public class TicketMaker {
    private int ticket = 1000;
    private static TicketMaker singleton = new TicketMaker();

    private TicketMaker() {
    }

    public static TicketMaker getInstance() {
        return singleton;
    }

    public synchronized int getNextTicketNumber() {
        return ticket++;
    }
}

TicketMakerクラスは、プライベートメンバ変数として、ticket(現在のチケット番号)と、staticなプライベートメンバ変数として、singleton(自身のインスタンス)を持ちます。

TicketMakerクラスのコンストラクタは何もしていません。

パブリックメソッドとしては、singleton(自身のインスタンス)を返すgetInstance() と、ユニークなチケット番号を返す getNextTicketNumber() が実装されています。

getNextTicketNumber() には、synchronized が付いていますが、これは、複数スレッドから同時に呼ばれても、順番に実行してくれる仕組みです。

この仕組みが入っていない場合、先に実行されたスレッドがチケット番号を返した後、チケット番号をインクリメントする前に、別のスレッドが割り込んで、同じチケット番号を返してしまう可能性がある、などの問題が発生するかもしれません(ということだと思います)。

TicketMakerクラスのクラス図

PlantUMLで作成したクラス図のコードと画像ファイルです。

@startuml
class TicketMaker {
    - int ticket
    - {static} TicketMaker singleton
    - TicketMaker()
    + {static} TicketMaker getInstance()
    + synchronized int getNextTicketNumber()
}
@endum

TicketMakerのクラス図
TicketMakerのクラス図

実行結果

最後に、実行結果を示します。チケット番号が、1000から被らない番号で、10個発行されていることが確認できます。

実行結果
実行結果

おわりに

今回はシングルトンパターンを学びました。

シングルトンパターンのメリットと、実用的な事例だったと思います。

クラス図のおかげで、毎回アイキャッチ画像に悩まなくて良さそうです(笑)

今回は以上です!

最後までお読みいただき、ありがとうございました。