Open4

テンプレートメソッドパターンを関数で置き換える

Nakano as a ServiceNakano as a Service

WEB+DB Press vol.132できしださんがオブジェクト指向神話からの脱却の一環として、テンプレートメソッドパターンをJavaのラムダ式で置き換える方法を紹介していました。
自分もなるほどなと思い、Web界隈に布教するため、TypeScriptに翻訳してみました。

Nakano as a ServiceNakano as a Service

以下の2つの関数は、drawOvalとdrawRectの部分以外は共通しています。

function threeCircles(interval: number) {
  const g = getGraphics();

  for (let i = 0; i < 3; i++) {
    g.drawOval(i * interval, 8, 8);
  }
}

function threeRectangles(interval: number) {
  const g = getGraphics();

  for (let i = 0; i < 3; i++) {
    g.drawRect(i * interval, 8, 8);
  }
}
Nakano as a ServiceNakano as a Service

この2つの関数を共通化するためにテンプレートメソッドパターンを適用すると、以下のようになります。

abstract class ThreeFigures {
  draw(interval: number): void {
    const g = getGraphics();

    for (let i = 0; i < 3; i++) {
      this.drawFigure(g, i * interval);
    }
  }
  abstract drawFigure(g: Graphics, x: number): void;
}

class ThreeCircles extends ThreeFigures {
  drawFigure(g: Graphics, x: number): void {
    g.drawOval(x, 8, 8);
  }
}

class ThreeRectangles extends ThreeFigures {
  drawFigure(g: Graphics, x: number): void {
    g.drawRect(x, 8, 8);
  }
}

function threeCircles(): void {
  const threeCircles = new ThreeCircles();
  threeCircles.draw(10);
}

function threeRectangles(): void {
  const threeRectangles = new ThreeRectangles();
  threeRectangles.draw(10);
}

見ての通りすごい行数が増え、大分見通しが悪くなりました。
drawメソッドとはなんぞやとIDEで実装に飛んでも、そこにあるのは抽象クラスの実装だけです。
あまり気軽に使えるパターンでは無いことがわかると思います。

Nakano as a ServiceNakano as a Service

これをコールバック関数を用いて関数的に共通化すると以下のようになります。

function threeFigures(
  interval: number,
  callback: (g: Graphics, x: number) => void
) {
  const g = getGraphics();

  for (let i = 0; i < 3; i++) {
    callback(g, i * interval);
  }
}

function threeCircles(interval: number) {
  threeFigures(interval, (g, x) => g.drawOval(x, 8, 8));
}

function threeRectangles(interval: number) {
  threeFigures(interval, (g, x) => g.drawRect(x, 8, 8));
}

とても見やすくなりました。