Kotlin でメソッドにするかトップレベル関数にするかとかの整理
(頭の中を整理するためのメモなので後で品質を上げる気はないです。)
一連の呼び出しの先頭の単語でコンテキストを絞れるのが良い。可読性の面でも良いし、補完も効きやすい。
また、広く知られた習慣があればそれに則っている事が重要。
更新系
オーソドックスなのは receiver.doSomething(args, …)
の形式。
レシーバーに対して操作をするというメタファーは広く認知されている。
レシーバーを先に書く事でコンテキストが絞られる。関数の補完がしやすい。
レシーバーが無い系
print(…)
みたいに何らかの更新を行う処理でトップレベル関数が使われる事もある。
よく使われていて、何が起こるのかが広く知られているような場合で使われそう。
「どこに対して print
してるのかって? 言わなくてもわかるだろ?」みたいな。
参照系
なんとなく、「生成系」と「その他」に分かれるイメージ。
「生成系」はトップレベル関数やコンストラクタ、コンパニオンオブジェクトの関数になる。
それ以外はオブジェクトに紐づいた関数(メソッド)になる。
多くの場合、「生成系」は引数より返り値の型の方がハイコンテキストなものになる。例えば createUser(String, Int): User
みたいに、汎用的なオブジェクトを引数にしてドメイン層のオブジェクトや View を作ったりする。
それ以外の場合は逆(String などを返す)になる。
汎用オブジェクトから汎用オブジェクトを返すやつ
String から String 返すものとか。
これもなんとなーくだけど、あるコンテキストでのみ意味がある関数と、完全に汎用的な関数として成り立つ関数がある。
後者はとりあえず良いんだけど、前者はトップレベル関数にするか拡張関数にするかで迷いがち。
例
-
"Taro Momo".firstName
なのか、firstNameOf("Taro Momo")
なのか -
"0312345678".normalizedTelephoneNumber
なのか、normalizedTelephoneNumberOf("0312345678")
なのか
上の例はちょっと恣意的だけど、結局は「暗黙的な『型』がどこに存在するか」で決めると良いのかな。
"Taro Momo"
は要するに単なる文字列というより「人名」だ。神経質に型を作るなら FullName
みたいな型を作りたくなる。それなら firstName
は拡張関数にした方が良さそう。
一方で 2. の方はアプリケーションで扱いやすいために標準化された「電話番号」を生成しようとしている。そうするとトップレベル関数の方がしっくり来る人が多そう。