JavaScriptにおけるデザインパターンとは、ソフトウェア開発において繰り返し発生する課題に対する、効果的な解決策を提供する手法やアプローチを指します。これらのパターンは、ソフトウェアの構造や設計をより効率的で維持しやすいものにするために利用されます。デザインパターンは、問題の解決方法を再利用可能な形で表現し、開発者が新たなソフトウェアを設計する際の指針を提供します。本記事では、JavaScriptにおける主要なデザインパターンを包括的に紹介し、それぞれのパターンがどのように機能するのかを詳しく解説します。
1. シングルトンパターン (Singleton Pattern)
定義
シングルトンパターンは、クラスに対してインスタンスが一度しか生成されないことを保証するデザインパターンです。通常、アプリケーション内で一度だけ存在するべきオブジェクト(例えば、設定管理やログ機能など)に使用されます。

使用例
設定情報を一元管理する場合、アプリケーション内で設定オブジェクトを一度だけ作成し、他の部分で共有することができます。
javascriptclass Settings {
constructor() {
if (Settings.instance) {
return Settings.instance;
}
this.config = {};
Settings.instance = this;
}
setConfig(key, value) {
this.config[key] = value;
}
getConfig(key) {
return this.config[key];
}
}
// 使用例
const settings1 = new Settings();
settings1.setConfig('theme', 'dark');
const settings2 = new Settings();
console.log(settings2.getConfig('theme')); // 'dark'
解説
Settings
クラスは、インスタンスが既に存在している場合には新しいインスタンスを作成せず、既存のインスタンスを返します。これにより、設定オブジェクトが1つだけ存在することが保証されます。
2. ファクトリーパターン (Factory Pattern)
定義
ファクトリーパターンは、オブジェクトの生成方法を隠蔽するためのパターンです。クラス名を直接呼び出すことなく、生成するオブジェクトのタイプを決定することができます。これにより、オブジェクトの作成方法を変更する際に、クライアントコードに影響を与えずに実装を変更できます。
使用例
異なる種類のユーザーを管理するシステムで、ユーザーの種類に応じて異なるオブジェクトを生成する場合に有効です。
javascriptclass Admin {
constructor(name) {
this.name = name;
this.role = 'Admin';
}
}
class User {
constructor(name) {
this.name = name;
this.role = 'User';
}
}
class UserFactory {
static createUser(type, name) {
if (type === 'admin') {
return new Admin(name);
} else if (type === 'user') {
return new User(name);
}
return null;
}
}
// 使用例
const admin = UserFactory.createUser('admin', 'Alice');
console.log(admin.name); // 'Alice'
console.log(admin.role); // 'Admin'
const user = UserFactory.createUser('user', 'Bob');
console.log(user.name); // 'Bob'
console.log(user.role); // 'User'
解説
UserFactory
クラスは、ユーザータイプに応じて Admin
または User
のインスタンスを生成します。これにより、クライアントコードはどのオブジェクトが生成されるかを気にせず、シンプルにファクトリーを利用できます。
3. モジュールパターン (Module Pattern)
定義
モジュールパターンは、JavaScriptでの名前空間の管理やコードのカプセル化を目的としたパターンです。このパターンを使うことで、変数や関数がグローバルスコープに漏れ出さないようにすることができます。
使用例
ユーティリティ関数や状態管理などをグローバルスコープから隔離し、外部からアクセス可能なインターフェースを提供する場合に使用されます。
javascriptconst CounterModule = (function () {
let count = 0;
return {
increment: function () {
count++;
},
getCount: function () {
return count;
}
};
})();
// 使用例
CounterModule.increment();
CounterModule.increment();
console.log(CounterModule.getCount()); // 2
解説
モジュールパターンでは、IIFE(即時実行関数式)を利用して、内部状態(count
)を外部から隠蔽し、提供するインターフェース(increment
と getCount
)だけを公開します。このように、モジュール内のデータを外部からの干渉なしに管理できます。
4. オブザーバーパターン (Observer Pattern)
定義
オブザーバーパターンは、あるオブジェクト(「通知元」)の状態が変化したときに、それに依存する複数のオブジェクト(「オブザーバー」)に通知を行うパターンです。このパターンは、イベント駆動型のプログラミングに適しています。
使用例
ユーザーインターフェースのイベントや、状態変更を複数の部分で反映させる場合に有効です。
javascriptclass Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
notifyObservers() {
this.observers.forEach(observer => observer.update());
}
}
class Observer {
update() {
console.log('State has been updated!');
}
}
// 使用例
const subject = new Subject();
const observer1 = new Observer();
const observer2 = new Observer();
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notifyObservers(); // 'State has been updated!' が2回表示される
解説
Subject
クラスは、状態の変化を監視するオブザーバーオブジェクトを保持し、状態が変化するたびに notifyObservers
メソッドで通知を行います。オブザーバーオブジェクトは、update
メソッドで通知を受け取り、必要な処理を行います。
5. ストラテジーパターン (Strategy Pattern)
定義
ストラテジーパターンは、アルゴリズムをクラスとして分離し、実行時に異なるアルゴリズムを選択できるようにするデザインパターンです。このパターンを利用することで、アルゴリズムの変更が他の部分に影響を与えることなく、柔軟に変更できるようになります。
使用例
異なる配送方法を選択できるシステムで、配送方法によって料金計算のアルゴリズムが異なる場合に使用されます。
javascriptclass ShippingCost {
constructor(strategy) {
this.strategy = strategy;
}
calculateShippingCost(weight) {
return this.strategy.calculate(weight);
}
}
class StandardShipping {
calculate(weight) {
return weight * 5;
}
}
class ExpeditedShipping {
calculate(weight) {
return weight * 10;
}
}
// 使用例
const standardShipping = new ShippingCost(new StandardShipping());
console.log(standardShipping.calculateShippingCost(10)); // 50
const expeditedShipping = new ShippingCost(new ExpeditedShipping());
console.log(expeditedShipping.calculateShippingCost(10)); // 100
解説
ShippingCost
クラスは、配送方法に応じたアルゴリズム(StandardShipping
または ExpeditedShipping
)を strategy
として受け取り、calculateShippingCost
メソッドでそれを適用します。このように、配送方法の変更はクラスを変更することなく、アルゴリズムを差し替えるだけで済みます。
まとめ
JavaScriptにおけるデザインパターンは、アプリケーションの設計をより良いものにするための強力な手段です。シングルトンパターンやファクトリーパターン、モジュールパターンなど、各パターンは特定の問題に対する効果的な解決策を提供します。これらのパターンを適切に使うことで、コードの再利用性や可読性、保守性を向上させることができます。