JavaScript中級

中級 JavaScriptで学ぶデザインパターン|練習問題編

導入

デザインパターンは、ソフトウェア開発において再利用可能な解決策を提供します。特に中級レベルのJavaScript開発者にとって、これらのパターンを理解し、実際のプロジェクトに適用することは、コードの可読性や保守性を向上させるために不可欠です。本記事では、実務で遭遇する具体的なシチュエーションを通じて、デザインパターンの実践的な利用方法を探ります。

教科書レベルの解説(デザインパターン)

重要な概念の整理

デザインパターンは、特定の問題に対する一般的な解決策です。例えば、オブジェクトの生成、構造、振る舞いに関するパターンがあります。中でも「ファクトリーパターン」は、オブジェクトの生成をカプセル化し、クライアントコードが具体的なクラスに依存しないようにします。これにより、コードの柔軟性が高まり、将来的な変更が容易になります。

コード例(JavaScript)


class Car {
    constructor(make, model) {
        this.make = make;
        this.model = model;
    }
}

class CarFactory {
    static createCar(make, model) {
        return new Car(make, model);
    }
}

const myCar = CarFactory.createCar('Toyota', 'Corolla');
console.log(myCar);

コードの行ごとの解説

  1. class Car { ... }: 車の情報を保持するクラスを定義します。
  2. class CarFactory { ... }: 車を生成するためのファクトリークラスを定義します。
  3. static createCar(make, model) { ... }: 車を生成する静的メソッドを持ち、クライアントはこのメソッドを通じて車を生成します。
  4. const myCar = CarFactory.createCar('Toyota', 'Corolla');: ファクトリーメソッドを呼び出し、新しい車のインスタンスを生成します。
  5. console.log(myCar);: 生成した車のインスタンスをコンソールに出力します。

練習問題編

以下の練習問題に取り組んで、デザインパターンの理解を深めましょう。

  1. 問題1: ファクトリーパターンを用いて、異なる種類の動物(犬、猫)を生成するクラスを作成してください。
  2. 問題2: シングルトンパターンを使って、アプリケーション内で一度だけインスタンス化される設定管理クラスを実装してください。
  3. 問題3: オブザーバーパターンを用いて、あるイベントが発生した際に複数のリスナーに通知するシステムを実装してください。

模範解答と解説

  1. 問題1の解答:
    
    class Animal {
        constructor(name) {
            this.name = name;
        }
    }
    
    class Dog extends Animal {
        bark() {
            return `${this.name} says Woof!`;
        }
    }
    
    class Cat extends Animal {
        meow() {
            return `${this.name} says Meow!`;
        }
    }
    
    class AnimalFactory {
        static createAnimal(type, name) {
            switch(type) {
                case 'dog':
                    return new Dog(name);
                case 'cat':
                    return new Cat(name);
                default:
                    throw new Error('Unknown animal type');
            }
        }
    }
    
    const dog = AnimalFactory.createAnimal('dog', 'Buddy');
    const cat = AnimalFactory.createAnimal('cat', 'Whiskers');
    
  2. 問題2の解答:
    
    class Settings {
        constructor() {
            if (Settings.instance) {
                return Settings.instance;
            }
            this.configuration = {};
            Settings.instance = this;
        }
    
        setConfig(key, value) {
            this.configuration[key] = value;
        }
    
        getConfig(key) {
            return this.configuration[key];
        }
    }
    
    const settings1 = new Settings();
    const settings2 = new Settings();
    console.log(settings1 === settings2); // true
    
  3. 問題3の解答:
    
    class EventEmitter {
        constructor() {
            this.listeners = {};
        }
    
        on(event, listener) {
            if (!this.listeners[event]) {
                this.listeners[event] = [];
            }
            this.listeners[event].push(listener);
        }
    
        emit(event, data) {
            if (this.listeners[event]) {
                this.listeners[event].forEach(listener => listener(data));
            }
        }
    }
    
    const emitter = new EventEmitter();
    emitter.on('dataReceived', (data) => {
        console.log('Data received:', data);
    });
    emitter.emit('dataReceived', { id: 1, message: 'Hello World' });
    

まとめ

  • ファクトリーパターンはオブジェクト生成の柔軟性を高める。
  • シングルトンパターンはインスタンスを一つに制限し、リソースの管理を容易にする。
  • オブザーバーパターンはイベント駆動型のプログラミングを可能にし、非同期処理の実装に役立つ。