はじめに
Sigletonパターン(GoFデザインパターン)について学習していた際に、Enumを使うやり方があったので、Enumについてさらっと復習しつつ、Singletonパターンについてほんの少々触れようと思います。
本記事ではしっかりまとめることはしていません。Enum, Singletonパターンについてなんとな~く知りたい方向けになります。
また、私自身が勉強中ですので、何か間違っていましたら教えていただけると嬉しいです。
まず、Enumの基本の基本
関連する複数の定数を、列挙子としてまとめて定義できるクラスが、Enum(列挙型)です。
コード全体の整理がしやすく、間違った値を使うリスクも減り、型安全性も向上するというメリットがあります。
さっそく、スポーツ関連の定数をまとめて定義してみます。
public enum SPORTS {
SOCCER,
BASKETBALL,
TENNIS
}
ここで定義した定数を、このようにして呼び出すことができます。
public class Main {
public static void main(String[] args) {
System.out.println(SPORTS.SOCCER);
}
}
// 出力: SOCCER
switch文で使う
Enumといえばswitch文かもしれませんね。
Enumを使えば、switch文の条件分岐を非常にわかりやすく記述することができます。
public class Main {
public static void main(String[] args) {
hoge(SPORTS.SOCCER);
}
protected static void hoge(SPORTS sportsType) {
switch (sportsType) {
case SOCCER:
System.out.println("サッカー");
break;
case BASKETBALL:
System.out.println("バスケットボール");
break;
default:
System.out.println("テニス");
}
}
}
// 出力: サッカー
フィールドやメソッドの定義
ここからが本題です。
Enumは、関連する複数の定数を、まとめて定義できるクラスなので、フィールドやメソッドを定義することができます。
列挙子に値を付与する場合はフィールドとコンストラクタを定義する必要があるのです。
以下では列挙子に値をもうけ、外からそれにアクセスできるようなgetterメソッドを作ってみました。
public enum SPORTS {
SOCCER("サッカー"),
BASKETBALL("バスケットボール"),
TENNIS("テニス");
// フィールド
private String japanese;
// コンストラクタ
private SPORTS(String japanese) {
this.japanese = japanese;
}
// getterメソッド
public String getJapanese() {
return japanese;
}
}
public class Main {
public static void main(String[] args) {
System.out.println(SPORTS.SOCCER.getJapanese());
}
}
// 出力: サッカー
この時、コンストラクタは必ずprivate
でないといけません。
なぜなら、Enumの列挙子たちは、それぞれクラスロード時に生成される単一のインスタンスであるからです。
なので、外部からnewして新しくインスタンスを作ることはできないよう、Javaの言語仕様で決まっているのです。
あれ?単一のインスタンスのみしか作れない決まりがあるクラスって、、、
Singletonだ!
ここからSingletonパターンのお話です!
GoFのデザインパターンの中でも、よく使うものとして頻繁に名前の挙がる設計パターンですね。
Singletonパターンというのは、単一のインスタンスのみの生成を保証し、そのインスタンスへグローバルなアクセスを可能にする設計です。
「システム全体でただ一つのインスタンスを作りたい」「唯一の存在であるべきものを作る」という場合に利用されます。例えば、設定や構成情報の管理、ログ管理の場面で活躍するそうです。
デフォルトでコンストラクタはpublic
になってしまうので、きちんとprivate
で宣言しておきます。
public class Singleton {
// 唯一のインスタンス
private static final Singleton instance = new Singleton();
// コンストラクタ
private Singleton() {}
// インスタンスを返す、外からアクセス可能なメソッド
public static Singleton getInstance() {
return instance;
}
// 何かの処理をする、外からアクセス可能なメソッド
public void doSomething() {
System.out.println("何かの処理");
}
}
public class Main {
public static void main(String[] args) {
Singleton.getInstance().doSomething();
}
}
// 出力: 何かの処理
もう一度、先ほどフィールドとメソッドを定義したEnumの例を考えてみると、そっちでも似たようなことをしていましたよね。private
なフィールドとコンストラクタを定義し、インスタンスに外部からアクセスできるgetterメソッドを作っていました。
Enumの列挙子は単一のインスタンスでしたね。
ということは、これと全く同じことが、Enumで簡単に書けてしまうのではないでしょうか。
EnumでSingletonパターン
public enum Singleton {
// 唯一のインスタンス
INSTANCE;
// 何かの処理をする、外からアクセス可能なメソッド
public void doSomething() {
System.out.println("何かの処理");
}
}
public class Main {
public static void main(String[] args) {
Singleton.INSTANCE.doSomething();
}
}
// 出力: 何かの処理
とても簡単に書けてしまいました。
enumの列挙子はクラスロード時にJVMが自動で唯一のインスタンスを生成します。
そのため、通常のSingletonパターンで必要な「コンストラクタのアクセス制限」が必要ありません。
また、列挙子は外から直接アクセスすることが可能なので、「getInstance()メソッド」も不要になります。
まとめ
EnumとSingletonパターンの基本と、EnumによるSingletonパターンをさらっと見てきました。実際に使っていくためには、それぞれまだまだ深めていく必要がありますが(特にSingletonパターン!)、ひとまず本記事ではここまでにします。
参考にしたサイト