Spring
2022.08.25
シングルトンの使いどころは?仕組み・実装方法まで詳しく解説!
2023.11.18

この記事は「Javaで学習するSpringによるWebアプリ開発」というトレノキャンプのeラーニング講座を基に解説した記事です。

Springの学習前に押さえておきたいJava基本文法

本記事では、Springの学習前に押さえておきたいJavaの基本文法の中でも、「シングルトン」というデザインパターンについて解説していきます。

本記事で扱う内容・シングルトン

デザインパターンというのは、プログラムの設計において様々な目的のために確立された手段・方法に名前を付け、パターン化したものです。


その中でもシングルトンとは、あるクラスのインスタンスを2つ以上作成できないようにすることで、「どこからアクセスしても常に同一のインスタンスが参照される」ことを保証するデザインパターンです。


1.クラスオブジェクトとインスタンスオブジェクト

シングルトンについてみていく前に、インスタンス化の仕組みについて少し掘り下げます。


プログラムを作る際に意識されることはほとんどありませんが、クラスのインスタンスを生成する前に、まずはメソッドなどクラスの情報を持つクラスオブジェクトが1つ生成されています。

シングルトンのイメージ図

直接的にはこのクラスオブジェクトが「型」となって、インスタンスオブジェクトが生成されます。つまり、クラスオブジェクトが1つなのに対して、インスタンスオブジェクトは通常、これまでにも確認した通り複数個作成できます。


ただ、インスタンスを複数作成可能ということと、インスタンスを複数個作るかは別の問題です。例えばデータベースにアクセスするための仲介を行うオブジェクトは、それぞれのクラスで都度インスタンスを生成するのではなく、1つのインスタンスを生成しておいて、複数クラスでそれを使い回せばよい話です。



しかし、逆にこういったケースでは「インスタンスを複数作成可能」という仕様がネックとなる場合があります。設計者がどのような意図を持っていたとしても、仕組み上許されている時点で使用者が複数インスタンスを作成してしまう可能性があるためです。


そのような場合に用いるのが、今回利用する「シングルトン」です。

シングルトンは、あえて「インスタンスを1つしか作成できない」設計にすることで、クラスの使用者に1つのインスタンスを使い回すことを強制します。



2.シングルトンの仕組み

シングルトンの実装例

それでは、MemberServiceImplクラスを例として、シングルトンの仕組みを確認します。


まず、シングルトンは自身のインスタンスを静的フィールドに保持しています。

このようにすることで、どこからでもアクセス可能なインスタンスを作成できます。ただし、この時点ではprivateなフィールドのため直接アクセスすることはできません。



次に、最大のポイントとして、コンストラクタにprivate修飾子が指定されており、外部から新たにインスタンスを生成できなくなります。そのため、この時点で静的フィールド上に保持しているものが唯一のインスタンスとなります。


そして最後に、唯一のインスタンスへアクセスするためのstaticなgetterが定義されています。外部からシングルトンのインスタンスを取得するためには、newの代わりにこのgetterを呼び出すことになります。



それでは、ここまで確認できたら実際にコードを修正していきましょう。

今回修正するコードは、同シリーズの記事で実装したものです。同じものを実装したい場合は下記のリンクからご覧ください。


3.シングルトンの実装


それでは、唯一のインスタンスを提供する「シングルトン」パターンを実装していきます。


この記事では、実装に統合開発環境のEclipse 2019を使用します。

環境構築の手順は下記の記事でも解説しています。興味のある方は下記のリンクからご覧ください。

①    クラスにシングルトンパターンを適用

MemberServiceImpl.javaを開いてください。

package service;

import java.util.ArrayList;

import entity.Member;

public class MemberServiceImpl implements MemberService {

	private static MemberServiceImpl singleton = new MemberServiceImpl(); //唯一のインスタンス

	private MemberServiceImpl() {}; //コンストラクタはprivate修飾子でアクセスを制限

	public static MemberServiceImpl getInstance() { //唯一のインスタンスを返すgetter
		return singleton;
	}

	@Override //アノテーション
	public String greet(int i) {
		String[] greetings = {"Good Morning", "Hello", "Good Evening"};
		return greetings[i];
	}

	@Override //アノテーション
	public ArrayList<Member> getAll() {
		ArrayList<Member> list = new ArrayList<>();
		Member mem1 = new Member(1, "Linda", "linda@example.com");
		Member mem2 = new Member(2, "James", "james@example.com");
		list.add(mem1);
		list.add(mem2);
		return list;
	}

	@Override
	public int sumOf(int x, int y) {
		int sum = 0;
		for(int i = x; i<=y; i++) {
			sum += i;
		}
		return sum;
	}

}

まずは、唯一のインスタンスを保持するstaticフィールド singletonを宣言し、同時にnewでインスタンスを代入します。


続いてコンストラクタを定義しています。ここで重要ポイントです。外部からインスタンスを生成できないようにするため、コンストラクタにprivate修飾子を指定します。

最後に、唯一のインスタンスsingletonを返すgetter、getInstance()を定義しています。こちらは、どこからでもアクセスできるようpublicとしています。


以上でMemberServiceImplへシングルトンパターンを適用できました。

②    Mainクラスで出力を確認

続いて、Main.javaを見ていきます。

Main.javaの11行目のMemberServiceImplに赤線が表示されている

MemberServiceImplのコンストラクタはprivateに指定したため、newでコンストラクタを呼び出している部分でエラーが起きています。


シングルトンのインスタンスはnewではなく、getterで取得しなければならないので、この行は一旦コメントアウトしておきます。

Main.javaの11行目をコメントアウトしている

コメントにしたい行にカーソルを置いた状態で「Ctrl + /」を押すことで簡単にコメントアウトできます。


コメントアウトしたNewの代わりに、MemberServiceInpl.getInstance()でシングルトンのインスタンスを取得しています。変数名はserviceのままにしておいてください。

package demo;

import java.util.ArrayList;

import entity.Member;
import service.MemberServiceImpl;

public class Main {

	public static void main(String[] args) {
//		MemberServiceImpl service = new MemberServiceImpl();

		MemberServiceImpl service = MemberServiceImpl.getInstance();

		System.out.println(service.greet(2));

		System.out.println(service.getAll()); //ArrayListを直接出力する

		ArrayList<Member> list = service.getAll();
		for(Member mem : list) {	//拡張for文でlistの中身を一つずつ出力する
			System.out.println(mem.getId() + "," + mem.getName() + "," + mem.getEmail());
		}

		System.out.println(service.sumOf(3, 5));
	}

}

以上でシングルトンパターンの実装がすべて完了しました。


それでは、Main.javaを実行してみましょう。

EclipseでMain.javaを右クリックし、実行、Javaアプリケーションを選択

今回は、シングルトン適用前と出力が変わらなければ成功です。

Eclipseのコンソール画面

出力がシングルトンパターン適用前と変わっていないことが確認できました。

今回の解説・実装は以上です。



まとめ


最後に、ここまでの内容を簡単にまとめます。

まとめ

どれも重要な内容ばかりですので、曖昧な部分は復習してぜひこの機会に物にしておきましょう。


第1章 Springを扱うためのJava知識

1.Entityとは

Springを学ぶ前に知っておくべきJavaの知識について紹介します。
オブジェクト(Entity)の構成や、Entityクラスの実装方法を学びましょう。

2.クラスとインターフェース

Serviceクラス、インターフェースがどんなものなのか、またその実装方法を見てみましょう。
Javaのメモリ「ヒープ領域」と「スタック領域」についても解説します。

3.メソッドと引数

メソッドと引数について、問題に取り組みながら学びます。
作成したインターフェースとServiceクラスを使用して、メソッドの追加を行ってみましょう。

4.シングルトンとは【現在の記事】

「シングルトン」とは何なのか、インスタンスの化の仕組みについて掘り下げながら紹介します。
実装方法も確認しましょう。


第2章 Springの基本

1.講義環境を作る

以降アプリ制作を行うにあたって、Springを使う環境を作っていきましょう。
Gradleプロジェクトのインポート方法、プロジェクトを確認する方法を紹介します。

2.画面の出力方法

制作に慣れるため、SpringのMVCアーキテクチャの体験、データのブラウザへの出力を行いましょう。
それにともなって、ビルドツールの「Gradle」や記述方法の「アノテーション」、テンプレートエンジンの「Thymeleaf」についても解説していきます。

3.実践:画面の出力方法

手順に沿ってデータをブラウザに出力していきましょう。
アノテーションの付与、リクエストスコープが実際どのように使われているのか、Thymeleafを使う際の注意点などを確認していきます。

4.データベースの操作

Springからデータベースを操作する方法について確認します。
ここでは一番シンプルな、Controllerから直接データベースへアクセスしてデータを取得する処理を見ていきましょう。

5.実践:データベースの操作

実際にデータベースを操作します。
データベースのアクセスに用いるSpringJDBCクラスの使われ方を確認しましょう。




この記事をシェア