Adapterパターン

概要

インターフェースの違うオブジェクトを繋ぎ合わせるパターン

VanillaShake

interface VanillaShake {
  eat(): void;
}

Drink

interface Drink {
  drink(): void;
}

haveMeals

const haveMeals = (food: Food, drink: Drink) => {
  food.eat()
  drink.drink()
}

この引数に直接VanillaShakeを渡すことはできない

vanillaShakeAdapter

const vanillaShakeAdapter = (vanillaShake: VanillaShake) => ({
  drink: () => vanillaShake.eat()  
})

このメソッドにより、インターフェースの違うVanillaShakeDrinkとして扱えるようになる

haveMeals(food, vanillaShakeAdapter(vanillaShake))

使い所

  • すでに出来上がっていて変更コストが高いメソッドやクラスを別のインターフェースに当て嵌めたい時
  • 外部ライブラリなどで本体のコードを触りづらい時

Bridgeパターン

Adapterは2つの異なるinterfaceを繋げるパターン。

しかし、Bridgeパターンは処理の中身を外から注入するパターン

Bridgeを使わないと以下のようになってしまう

// リモコン
const tvController = {
  sendIr: () => {
    console.log("IR signal sent") // 赤外線を送る処理
  },
  turnOn: () => {
    sendIr()
    console.log("turn on tv")
  }
}

// 物理スイッチ
const tvSwitch = {
  turnOn: () => {
    console.log("turn on tv")
  }
}

const radioController = {
  sendIr: () => {
    console.log("IR signal sent") // 赤外線を送る処理
  },
  turnOn: () => {
    sendIr()
    console.log("turn on radio")
  }
}

const radioSwitch = {
  turnOn: () => {
    console.log("turn on radio")
  }
}

実行命令を送る方法(リモコンや物理スイッチ)が増えるとデバイス分の命令方法の定義が必要になる デバイスが増えると、同じような命令方法でも再度定義が必要になる

デバイスの動作(機能)と命令方法(実装)を分離して、命令にを外から注入するようにすれば、デバイス x 命令の定義が必要なくなる

interface Device = {
  turnOn: () => void;
}

const tv: Device = {
  turnOn: () => {
    console.log("turn on tv")
  } 
}

const radio: Device = {
  turnOn: () => {
    console.log("turn on radio")
  }
}

const createController = (device: Device) => ({
  turnOn: () => {
    console.log("IR signal sent") // 赤外線を送る処理
    device.turnOn()
  }
})

const createSwitch = (device: Device) => ({
  turnOn: () => {
    device.turnOn()
  }
})

const tvController = createController(tv)
const tvSwitch = createSwitch(tv)
const radioController = createController(radio)
const radioSwitch = createSwitch(radio)

参考