こんにちは。コミュニケーションアプリ「LINE」のモバイルクライアントを開発している石川です。
この記事は、毎週木曜の定期連載 "Weekly Report" 共有の第 6 回です。Weekly Report については、第 1 回の記事を参照してください。
乱切りか角切りか
以下のコードは、「もし ContactModel
が Person
タイプかつ "friend" 状態なら表示名 (displayName
) を取得する」というものです。
fun ...(contact: ContactModel): ReturnValue? {
val friendName = (contact as?
ContactModel.Person)?.takeIf {
it.isFriend
}?.let { normalizeEmoji(it.displayName) } ?: return null
// snip...
// snip...
}
なお、let
は Kotlin の標準の関数で、このコードの場合は以下のように振る舞います。
?.takeIf
の戻り値が null の場合: 何もせず null を返す?.takeIf
の戻り値が非 null の場合: 戻り値をit
としてnormalizeEmoji(it.displayName)
を呼び、その結果を返す
このコードで改善できる点はなにかありますか?
切る前にナイフを研ごう
上記のコードは不適切な改行によって、読みにくいものになっています。ロジックには全く手を加えずとも、改行の位置を変えるだけでも読みやすくすることができます。
val friendName = (contact as? ContactModel.Person)
?.takeIf { it.isFriend }
?.let { normalizeEmoji(it.displayName) }
?: return null
基本的には「意味が大きく区切られる」場所で改行するとよいでしょう。しかし、適切な改行位置を選ぶのが難しいときもあります。そのような場合は、コードを自然言語(英語や日本語など)に翻訳してみるのも一つの手段です。そうすることで、どこに大きな区切りがあるかが明確になることもあります。
上記のコードは以下のように翻訳できます。
If a contact is a "Person" and the person is a friend, take the name with normalizing; otherwise this returns null.
ここで、意味が大きく区切られる場所にスラッシュを挿入すると、以下のようになります。
If a contact is a "Person" / and the person is a friend, / take the name with normalizing; / otherwise this returns null.
そしてこれは、改善したコードの改行位置に対応します。
一方で、最初の読みにくい例の改行位置に対応するスラッシュを挿入すると、以下のようになります。
If a contact is a / "Person" and the person is / a friend / , take the name with normalizing; otherwise this returns null.
正しい位置に改行を入れることで、より発展的なリファクタリングが行えるようにもなります。1 行目の as? ContactModel.Person
と 2 行目の .takeIf
はフィルタの役割を果たしていて、4 行目の ?: return null
がそれに対応する結果になっています。そうすると、3 行目の normalizeEmoji(it.displayName)
はメソッドチェーンの外に移動してもよさそうということに気づけます。
val friend = (contact as? ContactModel.Person)
?.takeIf { it.isFriend }
?: return null
val friendName = normalizeEmoji(friend.displayName)