知っておいたほうが良いデザインパターン5つin TypeScript

最近[ちょうぜつソフトウェア設計入門――PHPで理解するオブジェクト指向の活用](https://www.amazon.co.jp/dp/B0BNH1J2W2)という本でデザインパターンの章があって、流石に最低限は覚えておかないと思ったのでメモ。

# デザインパターンって何？

デザインパターンは名前だけ知ってる状態な人のために軽く説明。

ソフトウェア設計でよく起きる問題に対する典型的な解決方法のこと。以下の特徴があります。

- 実装方法が決まっているアルゴリズムと違い、パターンの指針に従い自分で実装する必要がある。
- 実証・テスト済み
- 〇〇パターン使っているよ！と他人に伝えるだけで意思疎通しやすくなる

# 今回紹介するデザインパターンについて

- [refactoring.guru](https://refactoring.guru/ja/design-patterns/typescript)で上げられているパターンのうち、星が3かつ簡単なものを中心とした
- TypeScriptではあまり使われないもの/アンチパターンとされているものについては外している
- 10個も紹介したところで一気に覚えられないということで、5つだけに絞った

# 生成に関するパターン

## Builderパターン

- 複雑なオブジェクトを段階的に構築するパターン
  - -> プロパティの追加やリセットなど

### コード例

今回は会社が保有している案件（プロジェクト）の設定を想定して書いている。

<クラス図- Builderパターン>

```plantuml
@startuml
interface Project {
  + name: string
  + skills: string[]
}
class ExampleBuilder {
  - projects: Project[]
  --
  + reset(): void
  + projectFrontend(projectName: string): this
  + projectBackend(projectName: string): this
}
Project <|--* ExampleBuilder
@enduml
```

[typescript playground](https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgApQPYCsILMgbwChlkQ4BbCALmQGcwpQBzE+ga2ABsu7aGmIZgG0AukQC+RIgi5w6dZAFEAHpQAOXCACEArtwAm0Qm3VMAbnEjIz2XGD5pMOPGOQBeZGLYIMIAbp4GFAAFACUhFKk6roARlzACMhQEHQQYOG05hjABiakpGAAFsB0AHS2Lg4eXuKkUTZxCUmV9gBimOAQIAYhrXgAcpQ09IwsYbTFpfkFhSXl-Q4VunRFIcSzs+RUtItDVAA0bJsc3Ly0wgBEYACe6qkITOpglwfIlymIL2+XdAgKlzqmwkYQA3McCikwLooCBkFM6GwGjF4okbM57NpEOxur09sN+GMhBN4fMZrMERUMXgFis1hsTmQCei7INhkdGaceI4ruoiupXu85FA4OYIFxBZcKDc6ABHCVA2Yg8EnKEwuEIpGSaS+fz4WL6LhGKA1EAQADuyjUFE0OkNxvCRANhmgVNZYCxCBxPRClwMsWQFGAzBFYGAfhZVUuYTdVQ6fkgPo+EC+kfs0ZkfjoGC0ZS4GGYIWdRugYSAA)

<details>

<summary>コード例- Builderパターン</summary>

<div>

```typescript
interface Project {
  name: string
  skills: string[]
}

class ExampleBuilder {
  private projects: Project[] = []
  constructor() {}
  public reset(): void {
    this.projects = []
  }
  public projectFrontend(projectName: string): this {
      this.projects.push({
        name: projectName,
        skills: ["typescript", "react", "scss"]
      });
      return this
  }
  public projectBackend(projectName: string): this {
      this.projects.push({
        name: projectName,
        skills: ["php", "laravel", "mysql"]
      });
      return this
  }
}

const builder = new ExampleBuilder()
builder.projectBackend("db migration project").projectFrontend("react project")
console.log(builder)
```

</div>

</details>

## Factoryパターン

- 作成ロジックを外部に公開せず、内部でオブジェクトやクラスを返すパターン
- api環境の切り替え（本物のapiから取ってくるのか、モックから取ってくるのか..など）やmoduleの取得に使える

### コード例

エンジニアが持っているモジュールを簡単に返却できるfactoryメソッドを作成した（クラスの中身を書いてないが、特別な理由がない限り、実際の業務では真似しないこと）。

<クラス図- Factoryパターン>

```plantuml
@startuml
interface Module {}
class EngineerModule {}
class ProgramingModule {}
Module <|-- EngineerModule
Module <|-- ProgramingModule
class ModuleFactory {
  + engineer(): EngineerModule
  + programing(): ProgramingModule
}
EngineerModule <|--o ModuleFactory
ProgramingModule <|--o ModuleFactory
@enduml
```

[typescript playground](https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgLIHsAmBXANigbwF8AoEhXOAZyuQFEQBzUCaDHfZYAWwAd9uEcLXZ5CpCtVoAFKOkZQ43UI1Gce-CIOFosY5MTKSaujhABiiMOigBPAyWTJe2AEa5gCZEOYhWUAAoASgcnJygIMGwoEGQ-AHd6JhY2PXxgx2RSJxd3T2c5BSUVYNCwiKiYuIhE2XlFZSY1CAynUlJydBAqMGRuNIsrG3sAXmrE5ssEazsMhC6qdHwAOlx5AP6zKZnbZZ8UwKCgoA)

<details>

<summary>コード例- Factoryパターン</summary>

<div>

```typescript
interface Module {}

class EngineerModule implements Module {}
class ProgramingModule implements Module {}

class ModuleFactory {
  public engineer() {
    return new EngineerModule()
  }
  public programing() {
    return new ProgramingModule()
  }
}

const moduleFactory = new ModuleFactory()
console.log(moduleFactory.engineer())
```

</div>

</details>

# 構造に関するパターン

## Facadeパターン

- Facade = 「うわべ」「見せかけ」　という意味の英単語（[英辞書](https://eow.alc.co.jp/search?q=facade)）。
- 複数のサブシステムの処理をFacadeクラス内で行い、Facadeクラスは自身のメソッドでなにか行うことはなく、あくまで単純なメソッドで外部向けの「窓口」として振る舞う。
- Factoryパターンとは違い、クラス/オブジェクトそのものを返すのではなく、複数のサブクラスのメソッドを実行するのがポイント。

### コード例

簡素なログイン・ログアウト処理を持ったSystemFacadeクラスを作成した。これでSystemFacadeクラスを窓口に、間接的にこれらの処理を呼び出せるようにしている。

<クラス図- Facadeパターン>

```plantuml
@startuml
class User {
  + setUser(): void
  + resetUser(): void
}
class SystemId {
  + setSystemId(): void
  + resetSystemId(): void
}
class SystemFacade {
  - user: User
  - systemId: SystemId
  --
  + engineer(): void
  + programing(): void
}
User <--o SystemFacade
SystemId <--o SystemFacade
@enduml
```

[typescript playground](https://www.typescriptlang.org/play?#code/MYGwhgzhAECqEFMBO0DeAoa0AOBXARiAJbDSIAu8yAFAJRoC+mOBxpSCFVSdj6T6UJBgBlAJ4RyCALYBJACZpmeQiTIJy4yTIW9UTLCrbQOFLVLny9TAUKjRzMgGJhgYeQiWGkRAG5gpaFxEJAAuOBDlH39AiAkLBXDHS2ZgAHsAO0kkXGByNJQ9ZixyAAsiCAA6YORoAF5oDIQAdwiaWmLoMorKuO1LesaWh3idKw6sAxZVUhA0gHMiDKKsEvKqmqRejW46Tu6qvoT5bc1Ryz3J5VY1Ofm03HIV1YPqkMrTHZDLl-Xe84UH04GmSugm0Bs6EEmUkZHOLjcHkGTVayQR7gQeyOzlcGMqdyWe3SWTSIAQ+IW1Gx0nRHg61Np5LuDyeHWJEFJTMpDNxdKAA)

<details>

<summary>コード例- Facadeパターン</summary>

<div>

```typescript
class User {
  public setUser() {}
  public resetUser() {}
}

class SystemId {
  public setSystemId() {}
  public resetSystemId() {}
}

class SystemFacade {
  private user: User
  private systemId: SystemId
  constructor () {
    this.user = new User()
    this.systemId = new SystemId()
  }
  public login() {
    this.user.setUser()
    this.systemId.setSystemId()
  }
  public logout() {
    this.user.resetUser()
    this.systemId.resetSystemId()
  }
}

const systemFacade = new SystemFacade()
systemFacade.login()
console.log(systemFacade)
systemFacade.logout()
console.log(systemFacade)
```

</div>

</details>

# 振る舞いに関するパターン

## Stateパターン

- オブジェクトの状態を外部から変更・取得などを可能にします
- フロントエンドを触っている人なら、コンポーネントのstate管理、ReactでいうRedux/Vue.jsでいうVuexといったstore管理ライブラリと似たことをしていると思えばわかりやすいと思います。

### コード例

仕事場をオフィスなのか、リモート（在宅）ワークなのを取得したり、変更できるコードを作成しました。

<クラス図- Stateパターン>

```plantuml
@startuml
interface State {
  + getState(): string
}
class OfficeState {
  + getState(): string
}
class FromHomeState {
  + getState(): string
}
State <|-- OfficeState
State <|-- FromHomeState
class WorkLocation {
  - state: State
  --
  + changeLocation(state: State): void
  + getLocation(): string
}
WorkLocation o--> State
@enduml
```

[typescript playground](https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgMpjpZBvAUM5AcwjHUwgAoBKALmQGcwpRDcBfXXBAGznvuQB5GDGBIyWYAFsADtwhSI4ARJR4CxUhkjUc+AsigkArlBDIA5AHcA9lADWyGFBtTkNkWIgWA3Po4cXLz8yABiLlIAEq4QqsjScgpKYCraavqaqrrqBkZgpubWdo7OrsgAFjG+-uycPHwCAOrFADI2CJjANuY5MswAbuQMaXSq+gjdjFDGCGB22foEYOXA9AB0jEMAvMggEFZCnuJp1DUECOVwIMRtHWBdIBSbkKNpVHoGyMurG2nIO88IGciCRbp1ugtPnkCl8VutAWtMicqDVAhMQIxkLYHGD7t1-rt9shmjj2uDHij0fQbPI1twbIQKNj7LiHojQWS8RSqEA)

<details>

<summary>コード例- Stateパターン</summary>

<div>

```typescript
interface State {
  getState(): string
}

class OfficeState implements State {
  getState() {
    return 'work from office';
  }
}

class FromHomeState implements State {
  getState() {
    return 'work from home';
  }
}

class WorkLocation {
  private state: State
  constructor() {
    this.state = new OfficeState()
  }
  changeLocation(state: State) {
    this.state = state
  }
  getLocation() {
    return this.state.getState()
  }
}

const workLocation = new WorkLocation()
console.log(workLocation.getLocation())
```

</div>

</details>

## Strategyパターン

- Strategy（戦略）を切り替えたり、実行できるパターンになります。

### コード例

ここで使用するデザインパターンを設定するという想定で記載したコードとなります。

<クラス図- Strategyパターン>

```plantuml
@startuml
interface Strategy {
  + doAction(): void
}
class FacadeStrategy {
  + doAction(): void
}
class StateStrategy {
  + doAction(): void
}
Strategy <|-- FacadeStrategy
Strategy <|-- StateStrategy
class Programmer {
  - strategy: Strategy
  --
  + changeStrategy(strategy: Strategy): void
  + request(): void
}
Programmer o--> Strategy
@enduml
```

[typescript playground](https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgMpinSBzAnsgbwChlkATAewEEExgKQAKASgC5kA3C4MogXyJEEAGzgBnMcgBiiOGQjpMOfMAC2AB2ERVEcJMVYIeQiXLVa9JsxOlSCBmIpaAdMIrZGAckrJ4COSjqWJBQIJ7MANymAgJCohJoYIYGyshqmtq6YPoYhsbEpJQ0dAwsNrb2II4ubh7eFMhiSZDIQWAhYZHR-MiCIuKSAApQ7piqOlDl6lDAHIaNucrsKUa4ppVNUACutBRQZQW2YAAWwGLOm3n4ALzIIBAA7tKy8it4LN12x3Ag2AqLq0YlyWiSUq2sh1IJzOFwBxluwNWn2QUAgAEcthAmgdTKRUWAtqFkNDzoi8M4ihZSsxurENmBWiNsGMJshbvcnsNRnBxtAPhsnBBXO5GNNubyoM5URisWAWMwgA)

<details>

<summary>コード例- Strategyパターン</summary>

<div>

```typescript
interface Strategy {
  doAction(): void
}

class FacadeStrategy implements Strategy {
  doAction() {
    console.log('do facade pattern');
  }
}

class StateStrategy implements Strategy {
  doAction() {
    console.log('do state pattern');
  }
} 

class Programmer {
  private strategy: Strategy
  constructor() {
    this.strategy = new FacadeStrategy()
  }
  changeStrategy(strategy: Strategy) {
    this.strategy = strategy
  }
  request() {
    return this.strategy.doAction()
  }
}

const programmer = new Programmer()
console.log(programmer.request())
```

</div>

</details>

# まとめ

デザインパターンに疎い筆者の観点で、5つ挙げてみました。バックエンド、フロントエンド…といった領域問わず参考にしていただけると幸いです。

# 参考サイト

- [refactoring.guru](https://refactoring.guru/ja/design-patterns/typescript)
  - 最近日本語化されました。機械翻訳ではなく、しっかりとした日本語で説明されているためオススメです。 
- [Fireship - 10 Design Patterns in TypeScript](https://fireship.io/lessons/typescript-design-patterns/)