デザインパターンとは | 「JavaScriptデザインパターン」読書メモ

前提

2013年の書籍のため、ES5以前のJavaScriptについて解説しています。

現代のJavaScript(ES6以降)とは記法が異なる部分がありますが、デザインパターンの考え方は今でも有効です。

デザインパターンとは

デザインパターンとは、ソフトウェア設計における典型的な問題に対する再利用可能な解決策のことです。

デザインパターンの利点

  • 再利用性: 実証済みの解決策を再利用できる
  • コミュニケーション: 共通の語彙で設計を議論できる
  • 保守性: 理解しやすく、保守しやすいコードになる

デザインパターンの分類

GoF(Gang of Four)のデザインパターンは、以下の3つに分類されます:

  1. 生成パターン: オブジェクトの生成に関するパターン
  2. 構造パターン: オブジェクトの構造に関するパターン
  3. 振る舞いパターン: オブジェクトの振る舞いに関するパターン

主要なデザインパターン

1. Constructorパターン

オブジェクトを生成するための基本的なパターンです。

function Car(model, year, miles) {
  this.model = model
  this.year = year
  this.miles = miles
}

Car.prototype.toString = function() {
  return this.model + ' has done ' + this.miles + ' miles'
}

const civic = new Car('Honda Civic', 2009, 20000)
const mondeo = new Car('Ford Mondeo', 2010, 5000)

console.log(civic.toString())
console.log(mondeo.toString())

2. Moduleパターン

プライベートとパブリックのメンバーをカプセル化するパターンです。

const myModule = (function() {
  // プライベート変数
  let privateVar = 'I am private'
  
  // プライベート関数
  function privateMethod() {
    console.log(privateVar)
  }
  
  return {
    // パブリック変数
    publicVar: 'I am public',
    
    // パブリック関数
    publicMethod: function() {
      privateMethod()
    }
  }
})()

myModule.publicMethod() // "I am private"
console.log(myModule.publicVar) // "I am public"
console.log(myModule.privateVar) // undefined

3. Singletonパターン

クラスのインスタンスが1つだけであることを保証するパターンです。

const Singleton = (function() {
  let instance
  
  function createInstance() {
    const object = new Object('I am the instance')
    return object
  }
  
  return {
    getInstance: function() {
      if (!instance) {
        instance = createInstance()
      }
      return instance
    }
  }
})()

const instance1 = Singleton.getInstance()
const instance2 = Singleton.getInstance()

console.log(instance1 === instance2) // true

4. Observerパターン

オブジェクトの状態変化を他のオブジェクトに通知するパターンです。

function ObserverList() {
  this.observerList = []
}

ObserverList.prototype.add = function(obj) {
  return this.observerList.push(obj)
}

ObserverList.prototype.count = function() {
  return this.observerList.length
}

ObserverList.prototype.get = function(index) {
  if (index > -1 && index < this.observerList.length) {
    return this.observerList[index]
  }
}

function Subject() {
  this.observers = new ObserverList()
}

Subject.prototype.addObserver = function(observer) {
  this.observers.add(observer)
}

Subject.prototype.notify = function(context) {
  const observerCount = this.observers.count()
  for (let i = 0; i < observerCount; i++) {
    this.observers.get(i).update(context)
  }
}

5. Factoryパターン

オブジェクトの生成ロジックをカプセル化するパターンです。

function VehicleFactory() {}

VehicleFactory.prototype.vehicleClass = Car

VehicleFactory.prototype.createVehicle = function(options) {
  if (options.vehicleType === 'car') {
    this.vehicleClass = Car
  } else {
    this.vehicleClass = Truck
  }
  
  return new this.vehicleClass(options)
}

const carFactory = new VehicleFactory()
const car = carFactory.createVehicle({
  vehicleType: 'car',
  color: 'yellow',
  doors: 6
})

現代のJavaScriptでの実装

ES6以降では、クラス構文やモジュールシステムが導入され、より簡潔に書けるようになりました。

ES6のクラス構文

class Car {
  constructor(model, year, miles) {
    this.model = model
    this.year = year
    this.miles = miles
  }
  
  toString() {
    return `${this.model} has done ${this.miles} miles`
  }
}

const civic = new Car('Honda Civic', 2009, 20000)

ES6のモジュール

// module.js
const privateVar = 'I am private'

function privateMethod() {
  console.log(privateVar)
}

export const publicVar = 'I am public'

export function publicMethod() {
  privateMethod()
}

まとめ

デザインパターンは、ソフトウェア設計における共通の問題に対する実証済みの解決策です。

JavaScriptの進化により記法は変わりましたが、パターンの本質は変わりません。

適切なパターンを選択し、状況に応じて適用することが重要です。