条件を改善するためにはオブジェクトリテラルを使ってみよう

長年にわたり開発されてきたシステムは、非常に成熟していることが多いですが、そのために保守作業に充てる時間がなかなか取れない場合があります。また、システムのコードの可読性や品質についても、十分に注意が払われていないことがあります。

そんなレガシーシステムでもテストの導入はさることながら、コード自体を改善して「エラーのリスク減少」「拡張の容易さ」「保守性」といったことに焦点をあてて、条件の改善できるポイントをご紹介します。

今回は、条件を改善するために[オブジェクトリテラル（`object literals`）](https://typescriptbook.jp/reference/values-types-variables/object/object-literal)を利用して、`JavaScript`や`TypeScript`でよく使用される`if/else`文や`switch`文の改善をします。

これらの条件式は使いやすく、すべての開発者に馴染みがありますが、複数の欠点があり、その結果、コードの品質や可読性が低下する可能性があります。

目的は、コード内で条件を定義するための追加のツールを持つことで、コードの品質を向上させることです。

## if/elseの条件の欠点

コード内で条件を定義するための最も簡単な方法の1つは、`if/else`文を使用することです。これはシンプルで簡潔であり、多くの場合最初の選択肢に該当します。

```ts
if (role === 'utilisateur') {
  // 何か処理を実行
}
```

しかし、この方法は、条件がより複雑になったり、可能な条件がいくつかある場合には制約が発生することがあります。


```ts
const getStatusMessage = (status: string): string => {
  if (status === "success") {
    return "成功しました。";
  } else if (status === "error") {
    return "エラーが発生しました。";
  } else if (status === "loading") {
    return "読み込み中...";
  } else {
    return "不明な状態です。";
  }
};

console.log(getStatusMessage("success")); // 出力：成功しました。
```

コードを改善し、可読性と保守性を高めるために、他の文を使用することができます。その利点と欠点は、`switch`文です。

## switch条件の欠点

`switch`文は、特定の条件が見つかった場合にコードを実行することができます。
もし、条件が見つからなかった場合は、デフォルトの指示を実行することが可能です。このデフォルトの指示は、通常、さまざまな条件の最後に宣言されます。

```ts
const getStatusMessage = (status: string): string => {
  switch (status) {
    case "success":
      return "成功しました。";
    case "error":
      return "エラーが発生しました。";
    case "loading":
      return "読み込み中...";
    default:
      return "不明な状態です。";
  }
};

console.log(getStatusMessage("error")); // 出力：エラーが発生しました。
```

`switch`文の問題は、特定の条件が増えると冗長になりがちなことです。

さらに、各条件を独立して処理したい場合は、各ケースの終わりにbreak文を追加することを忘れないようにしなければなりません。
`switch`文を使用する際のもう一つの問題は、SOLID原則の[オープン/クローズドの原則](https://zenn.dev/andmorefine/articles/837d7c90f246ee)を遵守していないことです。

これらの問題に対処するための解決策として、[オブジェクトリテラル（`object literals`）](https://typescriptbook.jp/reference/values-types-variables/object/object-literal)を使用する方法があります。

## TypeScriptにおけるオブジェクトリテラルの利点

オブジェクトリテラルは、コード内で条件をより効率的に定義するために使用できます。
これは、Key/Valueのペアを格納するJavaScriptのオブジェクトです。

以下は、条件を定義するためのオブジェクトリテラルの使用例です。

```ts
const getStatusMessage = (status: string): string => {
  const statusMessages: { [key: string]: string } = {
    success: "成功しました。",
    error: "エラーが発生しました。",
    loading: "読み込み中...",
    default: "不明な状態です。",
  };

  return statusMessages[status] || statusMessages.default;
};

console.log(getStatusMessage("loading")); // 出力：読み込み中...
```

オブジェクトリテラルを使用することで、私たちは複雑な条件をより可読性が高く、保守性のある方法で定義することができます。

`if/else`文とは異なり、各条件を一つずつ確認する必要がありません。
さらに、新しいケースを追加したい場合は、オブジェクトに新しいエントリを追加するだけで済みます。

同様に、デフォルトの条件を追加したい場合は、デフォルトのKeyを持つエントリを追加することができます。

`switch`文と比較して、各ケースの終わりに`break`文を追加する必要もありません。また、オブジェクトリテラルはSOLID原則のオープン/クローズドの原則を遵守することを可能にします。

## 条件におけるオープン/クローズドの原則の適用

[オープン/クローズドの原則](https://zenn.dev/andmorefine/articles/837d7c90f246ee)は、SOLID原則の一部であり、保守性、拡張性、柔軟性のあるコードを書くための良いプラクティスのセットです。
この原則は、コードは拡張には開かれているが、修正には閉じられているべきであると述べています。

つまり、クラス、関数、またはモジュールに新しい動作や機能を追加する際に、既存のコードを修正する必要がないようにするべきです。これにより、コードが変更されるときにバグを導入したり、既存の機能を壊したりするリスクを制限できます。

例えば、`if/else`や`switch`の構造を使用する場合、新しい条件を追加するたびに、既存のコードを修正して新しいケースを処理しなければなりません。これではオープン/クローズドの原則に反してしまいます。

なぜなら、追加する際はコードを修正する必要があり、そのために以前は正常に機能していた動作に影響を及ぼす可能性があるからです。

以下は、オープン/クローズドの原則に従っていないswitchの例です。

```ts
const getStatusMessage = (status: string): string => {
  switch (status) {
    case "success":
      return "成功しました。";
    case "error":
      return "エラーが発生しました。";
    case "loading":
      return "読み込み中...";
    default:
      return "不明な状態です。";
  }
};
```

この例では、新しいステータス（例えば、`pending`）を追加したい場合、getStatusMessage関数を修正して新しいケースを追加する必要があります。これにより、エラーを引き起こすリスクが高まり、ケースが増えるにつれてコードの拡張性が低下します。

### オブジェクトリテラルを使用した例（オープン/クローズドの原則に準拠）

```diff_typescript
const statusMessages: { [key: string]: string } = {
  success: "成功しました。",
  error: "エラーが発生しました。",
  loading: "読み込み中...",
+  pending: "保留中です。",
  default: "不明な状態です。",
};

export const getStatusMessage = (status: string): string => {
  return statusMessages[status] || statusMessages.default;
};
```

この例では、`pending`のような新しいステータスを追加したい場合、statusMessagesオブジェクトに新しいKey/Valueを追加するだけで済み、getStatusMessage関数には手を加える必要がありません。
こうすることで、コードは拡張には開かれており（オブジェクトに新しいエントリを追加できます）、修正には閉じられている（関数自体はそのままの状態です）ことになります。

**オープン/クローズドの原則を遵守することで、以下の利点があります。**

- **エラーのリスクが減少**: 既存のコードを修正しないことで、すでに正常に機能している動作を壊すリスクを避けることができる
- **拡張の容易さ**: 新しい動作を追加することが簡単になり、コアのロジックに手を加える必要がないため、コードの拡張性が向上する
- **保守性**: コードがより理解しやすく、時間の経過とともにメンテナンスが容易になります。新しい開発者は、全体のロジックを理解して再記述することなく、既存の構造（例えば、statusMessagesオブジェクト）を変更することで新しいケースを追加できる

## まとめ

オブジェクトリテラルは、複雑または長い条件を定義するための強力なツールです。これにより、コードをより読みやすく、保守しやすくし、SOLID原則を遵守することができます。
もちろん、すべてのツールと同様に、この技術は必要な場合に使用し、最も適切な方法として使用することが重要です。
通常の業務を考えると、今回出したような簡単な条件（サンプル）は存在せず、複数の異なる条件が該当することが多いです。したがって、適宜条件を見直し、原則に準拠しているかどうかを確認しましょう。

そのため、すべてをオブジェクトリテラルにする必要はありません。

簡単な条件がある場合は、`if/else`文を使用するのが望ましいです。複雑な条件がある場合は、オブジェクトリテラルを利用することを検討して、業務に役立ててもらえれば幸いです。