デザインパターンをTypeScriptで学ぶ（TemplateMethod）

デザインパターンと言えば [TECHSCORE](https://www.techscore.com/tech/DesignPattern/index.html/) さんの記事が有名ですね。時々「このコード、何かのパターンで表現できそう...」と思うのですが、日常業務でデザインパターンをサッとかっこよく使う事ができていません。という訳で TECHSCORE さんの記事を読みつつ、自分の中で噛み砕いた知識を TypeScript のコードで書き起こしてみようと思います。

- 対象読者：Java のコードを読むのに苦戦する人（自分！！）
- 環境：typescript 4.0

23 パターン全部まとめたら記事同士のリンクを貼ろうと思います。

# TemplateMethod パターン

タイトルと本文の情報を、マークダウンまたは HTML にフォーマットする例です。

```TypeScript
/*
AbstractClass
*/
abstract class ItemFormatter {
  constructor(protected item: Item) {}

  format(): string {
    return this.getTitle() +
      this.getDivider() +
      this.getList() +
      this.getDivider() +
      this.getBody();
  }
  abstract getTitle(): string;
  abstract getList(): string;
  abstract getBody(): string;
  abstract getDivider(): string;
}

interface Item {
  title: string;
  list: string[];
  body: string;
}

/*
ConcreteClass
*/
class MarkdownItemFormatter extends ItemFormatter {
  getTitle = () => "#" + this.item.title;
  getList = () => this.item.list.map(item => `- ${item}`).join("\r\n");
  getBody = () => this.item.body;
  getDivider = () => "---";
}

class HtmlItemFormatter extends ItemFormatter {
  getTitle = () => `<h1>${this.item.title}</h1>`;
  getList = () => "<ul>" + this.item.list.map(item => `<li>${item}<li>`).join("") + "</ul>";
  getBody = () => `<div>${this.item.body}</div>`;
  getDivider = () => "<hr>";
}
```

# 説明

## AbstractClass

**TemplateMethod** で登場するのは抽象クラスです。抽象クラスは自身から派生したクラスのメソッドを呼び出す役割をします。

```TypeScript
/*
AbstractClass
*/
abstract class ItemFormatter {
  constructor(protected item: Item) {}

  format(): string {
    return this.getTitle() +
      this.getDivider() +
      this.getList() +
      this.getDivider() +
      this.getBody();
  }
  
  abstract getTitle(): string;
  abstract getList(): string;
  abstract getBody(): string;
  abstract getDivider(): string;
}

interface Item {
  title: string;
  list: string[];
  body: string;
}
```

## ConcreteClass

機能は派生クラスで実装します。この例では、マークダウンへのフォーマット、HTML へのフォーマットをそれぞれの派生クラスが専任します。

```TypeScript
/*
ConcreteClass
*/
class MarkdownItemFormatter extends ItemFormatter {
  getTitle = () => "# " + this.item.title;
  getList = () => this.item.list.map(item => `- ${item}`).join("\r\n");
  getBody = () => this.item.body;
  getDivider = () => "---";
}

class HtmlItemFormatter extends ItemFormatter {
  getTitle = () => `<h1>${this.item.title}</h1>`;
  getList = () => "<ul>" + this.item.list.map(item => `<li>${item}<li>`).join("") + "</ul>";
  getBody = () => `<div>${this.item.body}</div>`;
  getDivider = () => "<hr>";
}
```

## 利用例

```TypeScript
const item = {
  title: "2020/09/22 日報",
  list: ["10:30 チームミーティング"],
  body: "明日からやる気出します",
};

new MarkdownItemFormatter(item).format();

// # 2020/09/22 日報
// ---
// - 10:30 チームMTG
// ---
// 明日からやる気出します

new HtmlItemFormatter(item).format();

// <h1>2020/09/22 日報</h1>
// <hr>
// <ul><li>10:30 チームMTG</li></ul>
// <hr>
// <div>明日からやる気出します</div>
```

# このパターンが解決する問題

ありがちなのが `type` のような定数による分岐です。

```TypeScript
class ItemFormatter {
  format(item: Item, type: string): string {
    if (type === "markdown") {
      ...
    }
    if (type === "html") {
      ...
    }
  }
}
```

このようなケースでの if による分岐はお勧めしません。`&&` や `||` で安易に条件を追加しがちで、修正を重ねるごとに読みづらいコードに変化していきます。また一つのクラスの中であれこれ仕事をしようとするとコードが巨大になりがちで、ほんの少しの修正でもうっかり他のメソッドに影響を及ぼす可能性が出てきます。SOLID の原則でいう **修正に対してクローズド** に反した状態です。

**TemplateMethod** パターンは、共通化した振る舞いを「テンプレートメソッド」として抽出し分岐を派生クラスとして表現する事によって、修正をクローズドなものにできるパターンです。