ソフトウェア開発において、
可読性の向上において一番大切なことは、
強力なテクニック ——早期リターン——
「早期リターン」
- ハッピーパス 関数の主な目的を達成できるケース
- アンハッピーパス エラーなどで、
主な目的を達成できないケース
ハッピーパスの処理が関数中のどこにあるかが分かりやすいと、
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
}
...
}
今実装している機能の価値に大きく影響しないならば、
プログラミング原則やテクニックに対する考え方
「早期リターン」
書籍