ソフトウェア開発において、
可読性の向上において一番大切なことは、
強力なテクニック ——早期リターン——
「早期リターン」
- ハッピーパス 関数の主な目的を達成できるケース
- アンハッピーパス エラーなどで、
主な目的を達成できないケース
ハッピーパスの処理が関数中のどこにあるかが分かりやすいと、
fun someFunction() {
if (!isNetworkAvailable()) {
showNetworkUnavailableDialog()
return
}
val queryResult = queryToServer()
if (!queryResult.isValid) {
showInvalidResponseDialog()
return
}
... // ハッピーパスの実装
}
fun someFunction() {
if (isNetworkAvailable()) {
val queryResult = queryToServer()
if (queryResult.isValid) {
... // ハッピーパスの実装
} else {
showInvalidResponseDialog()
}
} else {
showNetworkUnavailableDialog()
}
}
まずは、if
によるネストの深い場所にハッピーパスがあるため、
また、isNetworkAvailable
がfalse
である)」ことに対応する処理は、if
のボディの下にあるelse
を辿ってみるまで分かりません。コード1ではアンハッピーパスと対応する処理が近くにあるため、
このように、
アンハッピーを取り除けばハッピーか?
強力なテクニックであるはずの
アンチパターン1:分かりにくい場所からのリターン
制御構造のネストの深い場所で早期リターンを行ったり、when
やswitch
の条件分岐の一部だけでリターンしたりすると、when
の一部に入ってしまっているため、
enum class ThemeType { LIGHT, DARK, INVALID }
fun setThemeBackgroundColor(themeType: ThemeType) {
val argbColor = when (themeType) {
ThemeType.LIGHT -> WHITE_ARGB_COLOR
ThemeType.DARK -> BLACK_ARGB_COLOR
ThemeType.INVALID -> return
// このreturnは見落とされやすい。
}
someView.setBackgroundColor(argbColor)
anotherView.setBackgroundColor(argbColor)
yetAnotherView.setBackgroundColor(argbColor)
}
コード3では、themeType
がINVALID
の場合においてのみ、return
を見落とすことは少ないかもしれません。しかし、return
を見落としやすいコードになってしまうでしょう。
早期リターンを行っているかどうかは、return
の部分を目立たせるべきです。方法はいくつか考えられますが、ThemeType
が変更可能なら、
ThemeType
からINVALID
を削除する関数の引数として、INVALID
の代わりにnull
で受け取るとコード4のように、ThemeType?
は、ThemeType
の型という意味です。
enum class ThemeType { LIGHT, DARK }
fun setThemeBackgroundColor(themeType: ThemeType?) {
if (themeType == null) {
return
}
val argbColor = when (themeType) {
ThemeType.LIGHT -> WHITE_ARGB_COLOR
ThemeType.DARK -> BLACK_ARGB_COLOR
}
...
}
このように、return
をうまく解消できることがあります。
アンチパターン2:不要なアンハッピーパス
もう1つのアンチパターンとして、List
の各要素に対して処理をするmap
やforEach
といった関数は、map
やforEach
を使う多くの場合、if (list.
といった早期リターンは不要でしょう。
コード3とコード4では、INVALID
はあくまでもアンハッピーパスとして扱っていました。もし、INVALID
はLIGHT
にフォールバックするという仕様であるならば、コード5のようにINVALID
もハッピーパスとして取り扱うことができます。
fun setThemeBackgroundColor(themeType: ThemeType) {
val argbColor = when (themeType) {
ThemeType.DARK -> BLACK_ARGB_COLOR
ThemeType.LIGHT, ThemeType.INVALID ->
WHITE_ARGB_COLOR
}
...
}
今実装している機能の価値に大きく影響しないならば、
プログラミング原則やテクニックに対する考え方
「早期リターン」
書籍