プログラミングをする際に良く耳にする「可読性」という言葉があります。
私はプログラミング未経験から縁があってIT企業に入社し今はSEとして働いていますが、
ほぼ独学で身につけたスキルのため、「可読性の高いコード」や「読みやすいコード」と言われても、正直どういうコードが読みやすいの?というのが分かりませんでした。
この記事では、具体的な例を挙げながら、なぜ読みやすいのか?なぜ読みにくいのか?について考えたいと思います。
「可読性ってどうやって考えたらいいの?」と思っている昔の私(今もですが・・)のような方の引き出しの一つに加えてもらえたら嬉しいです。
具体例
例えば、映画のチケット料金を計算するプログラムを書く時、
以下のようなクラスがあったと仮定します。
- 上映映画クラス(MovieOnPlay)・・・チケットを計算する元になる上映時間等を含んだ映画のクラス。
- 料金計算クラス(PriceCalculator)・・・上映映画を引数にとって、上映映画クラスの情報を元にチケット料金を計算する。
/**
* 上映映画クラス.
*/
class MovieOnPlay {
/** 映画の名前. */
private String name;
/** 上映開始時間. */
private LocalDateTime startTime;
/** 上映開始時間getter. */
public LocalDateTime getStartTime() {
return thie.startTime;
}
// この下にgetter、setterが続く想定(今回の記事には関係ないので省略)
}
/**
* チケット料金を計算する.
* @param movie 上映映画
* @return チケット料金
*/
public Integer calculate(MovieOnPlay movie) {
LocalDateTime now = LocalDateTime.now();
// 引数で受け取ったmovieがnullの場合と、
// movieの上映開始時間がシステム日付(now)より後だった場合は
// RuntimeExceptionを投げるコードをここに記述する
// 料金計算ロジック
calculatePrice(movie);
}
エラーチェックを行うコードを書くことを考えてみる
料金計算クラス(PriceCalculator)で、料金計算を行う前に以下のエラーチェックを行う場合を考えます。
- 引数に受け取った上映映画クラスがnullだった場合はエラー
- 料金計算しようとしている映画の上映開始時間が、現在の時間より前だった場合(すでに上映開始時間が過ぎている場合)はエラー
上記のチェックを行うコードを書く場合、以下の2つの書き方は同じ動きをします。
// 書き方の候補①
LocalDateTime now = LocalDateTime.now();
if( !(movie != null && movie.getStartTime().isBefore(now)) ) {
throw new RuntimeException("正しい上映映画が選択されていません。")
}
// 料金計算ロジック
calculatePrice(movie);
// 書き方の候補②
LocalDateTime now = LocalDateTime.now();
if(movie == null || movie.getStartTime().isAfter(now)) {
throw new RuntimeException("正しい上映映画が選択されていません。")
}
// 料金計算ロジック
calculatePrice(movie);
では、どちらが”可読性が高い”と言えるでしょうか?
実は可読性が高いのは①であると考えることができます。
なぜ①の方が可読性が高いと言えるのか?
エラーにならない条件から考えてみる
後から別の担当者がこのコードを見た場合、おそらく「どうしたらcalculate(movie)が実行されるのかな?」という観点で見ることが多いと思います。
RuntimeExceptionがスローされる条件より、calculate(movie)が実行される条件の方が、実際の業務においては重要だからです。
- movieがnullではない
- startTimeがnowより前である
上記の2つの条件を満たせば、ifブロックをスルーしてcalculatePrice(movie)を実行することができます。
逆に、”そうでない場合”はRuntimeExceptionをスローします。
それをそのままコードに落とし込んだのが①です。
// movieがnullではない
movie != null
// startTimeがnowより前である
movie.getStartTime().isBefore(now)
// ”そうでない場合”はRuntimeExceptionをスロー
if( !(movie != null && movie.getStartTime().isBefore(now)) ) {
throw new RuntimeException("正しい上映映画が選択されていません。")
}
上記の!( ) をはずした「movie != null && movie.getStartTime().isBefore(now)」を満たしたものがcalculate(movie)を実行できます。
②をもう一度見てみる
②をのコードを「どうしたらcalculate(movie)が実行できるか?」という観点で見てみます。
if分の中のコードに”当てはまらない”条件であれば実行できるのですが、
「movie == null」 の逆はすぐわかるとしても、「movie.getStartTime().isAfter(now)」でない条件とは何か?を考えた時に、少し時間がかかるのではないでしょうか?
// 書き方の候補②
LocalDateTime now = LocalDateTime.now();
if(movie == null || movie.getStartTime().isAfter(now)) {
throw new RuntimeException("正しい上映映画が選択されていません。")
}
// 料金計算ロジック
calculatePrice(movie);
そのため、①の方が可読性が高いと考えられます。
まとめ
今回はif文でエラーチェックを行う際の”読みやすさ”について考えてみました。
実際の業務では上記のようなケースはよくありますし、私も仕事をしていく中でif文に”当てはまらない”条件を考えるのに苦労したことは何度もあります。
プログラミングを勉強していく中で、こういった考え方でコードを書けば後々わかりやすいのか!と納得したうちの一つだったので記事にしてみました。
プログラミングは奥が深いので必ずしもこの書き方が100%正しいとは言えないのですが、こういった考え方を知っていくことで少しでも”読みやすい”コードを書けるようになりたいなと思います。