ryubloblog’s diary

宮城県の仙台で働くプログラマーです。

読みやすいコードを書くために気をつけていること~part2

今回は前回の記事の続きになります。

ryubloblog.hatenablog.com

この記事では下記の項目についてまとめていきたいと思います。

  • ネストが深すぎないこと
  • 不要なコメントがないこと/コメントが適切に残されていること
  • 処理の共通化が行われていること

ネストが深すぎないこと

ここではif文のネストについてまとめていきたいと思います。
if文による条件分岐を書くことは多々あると思うのですが、このネストが深くなればなるほど
処理を追うことがとても大変になります。
実務上ではあまりない例にはなりますが、下記ページとボタンの種類によって処理を分ける例になります。

type PageType = '一覧' | '新規登録' | '詳細' | '編集';
type ButtonType = '登録' | '更新' | '削除';

// NOTE: ページとボタンで処理を分ける関数
const buttonHandler = (page: PageType, button: ButtonType) => {
  if (page === '一覧') {
    return;
  } else if (page === '新規登録') {
    if (button === '登録') {
      // 登録処理
    }
  } else if (page === '編集') {
    if (button === '更新') {
      // 更新処理
    } else if (button === '削除') {
      // 削除処理
    } else {
      return;
    }
  } else {
    return
  }
};

見るからに処理を追うのが大変ですし、疲れます。
下記修正の例になります。

const buttonHandler = (page: PageType, button: ButtonType) => {
  if (page === '一覧' || page === '詳細') {
    return;
  }

  if (page === '新規登録' && button === '登録') {
    // 登録処理
    return;
  }

  if (page === '編集') {
    if (button === '登録') return;
    button === '更新'
      ? console.log(button) // 更新処理
      : console.log(button); // 削除処理
  }
};

早期リターンを使用したり、三項演算子を使用したり、条件を整理してあげることで、
処理が比較的わかりやすいソースコードになったかと思います。
switch文を使用してみるのも良い方法だと思っています。
個人的にですが、if文においてelse ifが2回以上出てくる場合は、
記述方法を修正した方が良い場合が多いと感じています。

不要なコメントがないこと/コメントが適切に残されていること

まず不要なコメントとは、ソースコードを見ればわかる内容のコメントのことです。
とてもシンプルな例ですが下記、新規登録か編集かで処理を分ける場合のコードになります。

// NOTE: 新規作成か編集で返す文字列を変更する
const title = isEdit ? '編集' : '新規作成';

明らかにこのソースのコメントは処理を見れば、何をしたいのかわかるので不要なコメントになります。

次にコメントが適切に残されていることについてですが、
最近実際に経験した内容を例に挙げたいと思います。
処理の内容としては、Reactで初回マウント時にのみ実行したい処理があったのですが、
ESLintの設定により記述したコードが怒られるといった場合に行った対処とそのコメントになります。

  useEffect(() => {
    // isEditの状態で行う処理が記述されているとする

    // NOTE: 初回マウント時のみ実行したいため、isEditは第2引数から除外する
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

このように、コメントにはなぜその処理を行ったかやなぜ必要なのかを記載することが、
適切であると考えています。
また、そのコメントの種類を表すためのアノテーションというものをつけると、
よりわかりやすいコメントになるかと思います。

処理の共通化が行われていること

処理の共通化は行うことは当たり前ではあると思うのですが、
通化を行っておくことで同じような処理を使い回すことができるようになるので、
メリットしかないです。むしろ共通化していないと確実にコードレビューで指摘されます。
下記例として、共通化を行っていない日付の開始と終了を更新する処理です。

const onChangeStartAtHandler = (startAt: Date | null) => {
  // startAt更新処理
};

const onChangeEndAtHandler = (endAt: Date | null) => {
  // endAt更新処理
};

それぞれ、受け取る値が違うだけでやりたいこととしてはほぼ同じです。
これを共通化した例が下記になります。

type DateType = 'startAt' | 'endAt';

const onChangeDateHandler = (date: Date | null, dateType: DateType) => {
  dateType === 'startAt'
    ? console.log(date) // startAt更新処理
    : console.log(date); // endAt更新処理
};

受け取った値とそのタイプにより処理を条件分岐しています。
ポイントとしては引数を増やして、条件分岐できるようにしたところになります。
引数を増やして共通化できるようになるパターンは比較的多いかと思います。
また、似たような処理を見つけた場合には、共通化できないかと日頃から考えることが大切だと思います。

最後に

今回は読みやすいコードを書くために気をつけていることについて、
2回に分けてまとめて見ましたが、日々コードレビューをされたり、レビューしたりする中で、
新たな発見があれば改めて記事にしていきたいと思います。
ここまで読んでいただきありがとうございます。