メソッド名の getXXX
と findXXX
どっちがいいの?みたいな話になることがある。
この手の話ができるだけでもいい感じだと思います。名前が記号化していないってことなので。 世の中には名前に力を割くのが無駄な文脈もあって、そう言うのに晒されて続けると当然そこに力をかけなくなります。 その文脈では最適解だけど、私は名前が重要だと思っているし、その価値観を土台に他のものを積み上げていきたい。 ということで話を戻す。
「得る」と「探す」のようなものを意図して使い分けるとコードが読みやすくなります。 使い分け方によって読みやすさは変わりはするのですが、「意図して使い分けている」だけでも十分変わります。 その意図に共感できたり汲み取れたりすればさらに読みやすくなりますが、その前段階として意図の有無が重要だと思ってます。
私の基本的な使い分け
getHoge(条件): Hoge
findHoge(条件): Optional<Hoge>
戻り値型に現れます。get
でOptional
はない感じ。
getXXX
は有ることが前提。無い場合は例外的事象として例外で扱います。
このメソッドの呼び出し側に見つからない場合を考慮させるべきではないので、非検査例外とします。
findXXX
は無い場合が例外的事象ではないので、戻り値で扱います。
戻り値 Optional
として、呼び出し側にハンドリングを要求します。
いま null
にするのは?
私が null
根絶穏健派(過激派じゃないよ)なので「使うな」にはなるんですが。
上記のような文脈の使い分けをするとして、見つからない場合に null
を返すのは避けることを推奨します。
getHoge(): Hoge
と findHoge(): Hoge
が並ぶと、使い分けが分かりません。
この場合はドキュメントでのフォローになりますが、コンパイル時の検査が効かなくなり、Javaを使っている利点が減衰してしまいます。
事実 null
を返すAPIはドキュメントでフォローされています。
たとえば java.util.Map#get(Object)
が挙げられ、このメソッドを使用する側は null
を考慮した実装をコンパイラではなく人間の注意深さでフォローする必要があります。 NonNull
のようなアノテーションとか静的解析、IDEのチェックなどで機械的なチェックがフォローされる範囲は広がってはいますが、まだまだ。
Optional
がない時代、 getXXX
は同じで findXXX
は null
を返すか検査例外のいずれかで、慣習的には null
が返されていたものが多かったように思います。
いまは Optional
があるのでこちらで扱いましょう。完璧な解ではないですが、 null
よりは良いです。
よそ見: Optional自身のgetの使い方
Optional
には find
とかないですが、 get
は empty
の場合に非検査例外を投げます。
ただ get
だけの名前だと危険が伝わりづらいため orElseThrow(SupplierSupplier
の記述がだるいのは理解できます。それもあってか、引数なしの orElseThrow()
メソッドがJava10(2018年3月)で追加され、 Optional
のJavadocでもこちらを使うように案内 されています。
なんとなく get
メソッドがあるクラスで Optional
を連想したのであげてみたけど、 Optional
に find
は合わなさそう。もし使われてたとしたら、 find
はメソッド呼び出し時に解決しにいくようなイメージを持ちそう。
よそ見: Jacksonのgetとfindの使い分け
Jacksonの JsonNode
を見てみましょう。Javadocはこちら。以下のようなメソッドがあります。
get(String fieldName): JsonNode
findValue(String fieldName): JsonNode
いずれも JsonNode
を返しますし、いずれも null
を返します。違いは get
が直下のみを探すのに対し、 findValue
は子要素も探すこと。スキーマが不定でないなら基本的に get
のみを使用するで良いと思います。
Jacksonのこの使い分けには「探す場所として指定されたパスに子要素も含むでしょ」のようなJSONがパスで表現できることを前提にしたAPIって感じですね。 そのライブラリがどういう世界観で言葉選びをしているかの過程を持っていると予測可能性が上がります。
よそ見: XXX
「よそがこうしているから自分たちもこうする」と言うのは「よそ」がいくら権威的なものであろうと微妙です。 よそはよそ。
でも「よそは(こう言う文脈だから)こう使ってるんだなぁ」と想像したり説明を試みてみるのは役立つと思います。 開発で使う語彙は中学英単語よりも少ないと思うし、目に入ったら眺めてみるといいと思う。
他の名前の候補
get
でも find
でもない何かいい感じの名前が見つかるならそれを試してみるのもいいです。 search
はたまにみる。
読みやすいコードのガイドライン 第2章 命名 では get
find
search
pop
calculate
fetch
query
load
などが挙げられています。これらの単語で置き換えできるかを試してみると名前が引き締まったりする。
書籍では「辞書や類語辞典」が挙げられていて、私はこういうのは英英辞典をよく使います。
search
を使うのは find
より探し回る感とか検索条件が広そうなイメージある。とは言えネイティブでない我々がニュアンスで使い分けるのは難しいので、チームやそのコードに関わるスコープで会話しながら調整したいところ。
参考
先に挙げた「読みやすいコードのガイドライン」や「良いコード/悪いコードで学ぶ設計入門」など、最近(といってもともに2022年ですが)の書籍でも命名はそれなりに割かれています。
同じようなことや逆のことを書いているように見えたりして面白くあります。私も共感するものもあれば、趣味に合わないなぁと思うところもしばしば。この2冊はGod/Badのような書き口。
名前の話は長年ずっと語られることで、10年遡ってみても「CleanCode」(邦訳2009年)では第1章「クリーンコード」の次にくる第2章が「意味のある名前」です。
あと「きのこ本」として知られていた「プログラマがしるべき97のこと」(2010年)の「名前重要」とか。
20年、30年遡ってもいくらでもあると思うんで、興味ある人は探してみよう。
前述のように英単語の選択に迷う場合は英英辞典ですが、用途的にポケットサイズのを使っています。Webでもいいのですが、周りの単語がヒントになることも多いので紙が好き。
あとニュアンスはこちらのニュアンス図鑑もいい感じです。 make
build
create
produce
とか。
ニュアンスだとコアイメージもたまにみてる。
別に英語的な正しさとかは重視しているわけではなくて、意図して使い分けるための根拠を私はこの辺に求めている感じです。 日本語名称の時は国語辞典のほか、語源とか漢字の由来とかを参照したりする。そいやこっちは書籍とか持ってないな……。
私はいい名前が思いつかないときは変な名前をつける - 日々常々 とブログで書いているように、名前はいきなりは思いつかないしどんどん変えて育てていくものだと思うのでプロセスも紹介しておく。
このプロセスの過程で一般的には「避けるべき」とされる名前が出ることもあるだろうし、すべての名前をこのプロセスでやるものでもないと思っている。
と言うことで、名前の使い分け
意思に基づいて名前が使われていることを望みます。
それは「同じものは同じ単語を使う」と言う形で現れるかもしれないし、他の形かもしれない。 ともかく意思に基づいていることが重要で、「可読性」がもたらす「予測可能性」を重視したいと思っています。 読みやすいコードとは驚きのないコード。驚きのないコードとは予測が当たるもの。そんな感じ。
get
/find
の本稿の使い分けはそれなりに受け入れられていると思うので扱いました。
こうしておくとコードを読むときに挙動の予測ができて紛れが減ります。
可読性に寄与すると言われる一般的なルールに反していても、予測可能性があれば読みやすいコードと言えると思う。
使い分けに迷ったら自身の経験を言語化してみたり、他のライブラリの使い方を参考にしてみるのもいいと思う。 でもその使い分け方が貴方のコンテキストに合うかは別の話。あくまで参考に。 Jacksonの例であげたように文脈が違えば使い分けで示したいものも変わってくるので、ライブラリやフレームワークを使う時にはその文脈の言葉をどこまで持ち込むかを設計する必要がでてきます。ものによってはコンテキストマッピングが必要になるかもしれない。
そんな感じ!(まとまらない)
追記
「基本的な」使い分けと書いている通り、他の使い分けをすることもあります。 重要なのは使い分け方が同じ文脈で混ざらないことかなーって。 多分言葉を尽くせば根源にある共通的な概念を言語化できるんだけど、しなくても伝わるでしょ、うん。
計算量
改めて考えると、自分は以下みたいなイメージで使い分けている気がする。
— 大雪 命 (@mikoto2000) 2024年5月30日
get ... O(1)
find ... O(x) : x > 1
が、ちょっと考えるだけで例外ある気がするなぁ... https://t.co/eh7AKyhH34
私も同じ感覚ある。
件数
良かった。
— shiryu (@shiryu_go) 2024年5月30日
自分の場合は今まで、
findだとList
getだと単一
が帰ってきそうなイメージだった https://t.co/HvuMV23w0O
私も同じ感覚ある。 get
が 1
に対して find
は List
で 0..n
と言う感覚。
件数での使い分けを考えると 0..1
, 1
0..n
1..n
を分けたくなって、 get
/ find
だけじゃ足りねぇとなり、戻り値型とか findOne
とか他の表現方法組み合わせてなんとかしようってやり始める。毎回何だかんだそれなりに落ち着くんだけど、決まった答えは持ってないかも。