Singleton
モジュールパス: /alier_sys/Singleton.js
概要
Singleton
クラスは、シングルトンパターンのクラスを実装するための基底クラスとして利用します。
- シングルトンパターンとは、インスタンスが1つだけであるようなクラスの設計パターンのことです。
Singleton
クラス自体はシングルトンパターンを実装するクラスではありませんが、アプリケーション上でクラスのインスタンスがただ一つに限られるようなクラスの作成を支援します。具体的には、以下の手順で作成されたクラスは単一のインスタンスだけを持ちます:
Singleton
を継承する- コンストラクタの中で
initialize()
メソッドを呼び出し、その返値をコンストラクタからreturn
するinitialize()
メソッドは引数initializer
として関数を受け取ります。引数として渡した関数はクラスインスタンスの初期化のために使います。initialize()
メソッドは、クラスインスタンスが未初期化の場合に限り、与えられたinitializer
を呼び出して初期化します。
実装例は以下の通りです:
// 1. Singleton を継承したクラスとして MySingleton を定義します。
class MySingleton extends Singleton {
constructor() {
// JavaScript の言語仕様上、super() の記述は必須です。
// また super() が値を返すまで、this を参照することはできません。
super();
// 2. this を対象として initialize() を呼び出し、その結果を return します。
// initialize() の引数の関数は、MySingleton のインスタンスが未初期化の場合にのみ、
// 呼び出されます。
// initialize() は常に同一の初期化済みの MySingleton インスタンスを返します。
// コンストラクタからの return は new MySingleton の結果として返ります。
return this.initialize(() => {
// 以下に初期化に必要な処理を書きます。
this.x = 42;
this.y = "foo";
});
}
}
実装した MySingleton
を利用する場合は、単に new
演算子を使ってインスタンスを取得します。
const mySingleton = new MySingleton();
こうして定義された MySingleton
のコンストラクタは常に同じインスタンスを返します。
従って、例えば new MySingleton() === new MySingleton()
は常に true
を与えます。
- 曖昧さをなくすため、本項およびその関連するページにおいては原則的に、フレームワークが提供するクラスを「
Singleton
クラス」と表記し、デザインパターンとしては「シングルトンパターン」と表記します。Singleton
を継承したクラスを 「Singleton
派生クラス」と表記します。Singleton
派生クラスのうち、実際にコンストラクタが常に同一のインスタンスを返すクラスを「シングルトンパターンを実装するSingleton
派生クラス」と表記します。Singleton
クラスのインスタンスを「Singleton
インスタンス」と表記します。
- シングルトンパターンを実装する
Singleton
派生クラスを継承して、新たな別のクラスを作ってはいけません。 基底側のクラスのインスタンスが常に同一であることを保証できなくなるためです。 - 一方で、シングルトンパターンを実装しない
Singleton
派生クラスは自由に継承できます。 基底側のクラスがインスタンスが同一であることを保証しないため、派生クラスがインスタンスを作っても矛盾が生じないためです。
コンストラクタ
構文
new Singleton()
解説
Singleton
クラスのインスタンスを生成します。
Singleton
(またはシングルトンパターンを実装していない Singleton
派生クラス)のコンストラクタからインスタンスを取得する場合、それは常に新しいインスタンスを生成します。常に同一のインスタンスを得るには、そのクラスがシングルトンパターンを実装する Singleton
派生クラスとして作られていなければなりません。
シングルトンパターンを実装する Singleton
派生クラスを作るには、そのクラスのコンストラクタから initialize()
を呼び出して、その結果を return
しなければなりません。
class MySingleton extends Singleton {
constructor() {
super();
return this.initialize(() => {
/* ここでオブジェクトを初期化します */
});
}
}
例
以下ではファイル RemotePreferences.js
でシングルトンパターンを実装する Singleton
派生クラスとして RemotePreferences
クラスを実装します(ただしインポートおよびエクスポートの記述などは省略しています)。
class RemotePreferences extends Singleton {
constructor() {
super();
return this.initialize(() => {
this.#theme = "polarized-light";
this.#muteSince = "21:00";
this.#muteUntil = "09:00";
this.#lastModified = "1970-01-01T00:00:00Z";
});
}
#theme;
#muteSince;
#muteUntil;
#lastModified;
get() {
return {
theme : this.#theme,
muteSince : this.#muteSince,
muteUntil : this.#muteUntil,
lastModified: this.#lastModified
};
}
async update(preferences) {
const {
theme : newTheme,
muteSince : newMuteSince,
muteUntil : newMuteUntil
} = preferemces ?? {};
const {
theme : oldTheme,
muteSince : oldMuteSince,
muteUntil : oldMuteUntil
} = this.get();
if (newTheme != null && newTheme !== oldTheme) {
this.#theme = newTheme;
}
if (newMuteSince != null && newMuteSince !== oldMuteSince) {
this.#muteSince = newMuteSince;
}
if (newMuteUntil != null && newMuteUntil !== oldMuteUntil) {
this.#muteUntil = newMuteUntil;
}
try {
await this.sync();
} catch(e) {
this.#theme = oldTheme;
this.#muteSince = oldMuteSince;
this.#muteUntil = oldMuteUntil;
throw e;
}
}
async sync() { /* リモートサーバとの同期処理が実装されています */ }
}
ファイル MyApp.js
で RemotePreferences
を利用するには以下のようにします(以下でクラス MyApp
の詳細などは省略しています):
class MyApp extends ViewLogic {
async messageHandler(msg) {
msg.deliver({
setTheme: async (msg) => {
const { theme } = msg.param && {};
const pref = new RemotePreferences();
await pref.update({ theme });
},
setMute: async (msg) => {
const { muteSince, muteUntil } = msg.param && {};
const pref = new RemotePreferences();
await pref.update({ muteSince, muteUntil });
},
disableMute: async (msg) => {
const ;
const pref = new RemotePreferences();
await pref.update({ muteSince: "", muteUntil: "" });
}
});
}
}
メソッド
initialize()
- 対象の
Singleton
派生クラスを初期化します。引数として関数を与えることで、追加の初期化処理を記述できます。