【TS】今さら聞けないブリッジパターン
はじめに
今回はブリッジパターン(Bridge Pattern)について解説します。
機能と実装を分離し、複数の方向にクラスを拡張できるようにするデザインパターンです。
ブリッジパターンとは?
TECHSCOREさんの解説があります。
以下、引用です。
第9章では
Bridgeパターンを学びます。Bridgeパターンとは、「Bridge」すなわち「橋」の役割を果たすパターンです。Bridgeパターンを利用することで、機能と実装を分離して、それぞれを独立に拡張することができるようになります。
例えば、あるmethodAというメソッドを持つクラスMyClassAは、methodAメソッドの実装が異なるMyClassASub1、MyClassASub2という2つのクラスによって継承されているとします。このとき、MyClassAにmethodBというメソッドを追加するために、MyClassBクラスというMyClassAを継承するクラスを作成したことを考えてください。
このとき、MyClassBでも、MyClassASub1、MyClassASub2で実装しているmethodAと同じ実装を利用したい場合、MyClassBクラスを継承するMyClassBSub1、MyClassBSub2といったクラスを作成する必要があります。今回のように、2つのクラスだけなら手間はそんなにかかりませんが、場合によっては、
MyClassAに機能を追加するためのサブクラスMyClassXを作成するたびに、何十というMyClassXSub・・・ というサブクラスを作成することが必要となります。Bridgeパターンは、機能を拡張するための階層と実装を拡張するための階層を分離することにより、このようなわずらわしさを解消し、拡張を容易にするものです。
ポイントは 「機能を拡張するための階層と実装を拡張するための階層を分離」 するということです。
具体的な例を見ていきましょう。
例題
以下のように食器を表すクラスがあるとします。
食器クラスを継承した「金属製食器」と「木製食器」があり、
さらにそれらを継承した「金属製の箸」や「木製のスプーン」があるとします。
/**
* 食器
*/
abstract class Dishware {
use = () => {
};
}
/**
* 金属製食器
*/
class MetalDishware extends Dishware {
}
/**
* 木製食器
*/
class WoodenDishware extends Dishware {
}
class MetalChopsticks extends MetalDishware {
use = () => {
console.log("hold");
}
}
class WoodenChopsticks extends WoodenDishware {
use = () => {
console.log("hold");
}
}
class MetalSpoon extends MetalDishware {
use = () => {
console.log("scoop");
}
}
class WoodenSpoon extends WoodenDishware {
use = () => {
console.log("scoop");
}
}
「食器」なので以降も「ガラス製」や「漆器」「プラスチック」など、様々な材質が増えていくかと思います。
さらに種類としても「フォーク」や「皿」「丼」などが追加されると、サブクラスは膨大な数になっていきます。
ブリッジパターンで実装
今回は「食器の材質」を切り出します。
MaterialImpleクラスを作成し、それを実装するMetalとWoodを作成します。
abstract class MaterialImple {
}
class Metal extends MaterialImple {
}
class Wood extends MaterialImple {
}
Dishwareは上記のMaterialImpleを持ちます。
/**
* 食器
*/
class Dishware {
private _materialImple: MaterialImple;
constructor(materialImple: MaterialImple) {
this._materialImple = materialImple;
}
use = () => {
}
}
さらにDishwareを継承したChopsticksとSpoonを作成します。
class Chopsticks extends Dishware {
use = () => {
console.log("hold");
}
}
class Spoon extends Dishware {
use = () => {
console.log("scoop");
}
}
ポイントは「食器の材質」を切り出したことです。
こうすることで「食器」の「材質」と「種類」が分離され、それぞれの追加・実装が行いやすくなります。
例えば、プラスチックの材質を追加したければMaterialImpleを継承したPlasticを作成するだけで済み、
種類としてフォークを追加したければDishwareを継承したForkを作成するだけです。
以前の実装方法だと、上記に合わせてPlasticSpoonやMetalForkといったサブクラスを作成する必要が出てきましたが、ブリッジパターンでは必要ありません。
まとめ
今回はTypescriptでデザインパターンの一つである Bridge Pattern について紹介しました。
特徴として「機能と実装」を分離することで、それぞれを独自に拡張できる点が挙げられます。
クラス構造が複雑になり、似たようなサブクラスを量産していると感じた場合には導入してみるといいかなと思います。


Discussion