今回はリーダブルコードの3章より「読みやすいコード」を書く方法として「誤解されない名前」について触れていきます。
内容紹介
3章 誤解されない名前
- 3章 誤解されない内容
- 3.1 例:filter()
- 3.2 例:Clip(text, length)
- 3.3 限界値を含めるときはminとmaxを使う
- 3.4 範囲を指定するときはfirstとlastを使う
- 3.5 包括/排他的範囲にはbeginとendを使う
- 3.6 ブール値の名前
- 3.7 ユーザの期待に合わせる
- 3.8 例:複数の名前を検討する
- 3.9 まとめ
3章では2章で取り扱った「命名」に対して、命名時に気を付ける点を深掘りして
- どのような名前だと「誤解される」のか?
- どういうことに気を付ければ「誤解されない」ようになるのか?
を解説しています。
本章はリーダブルコードにて具体例を上げているものが多いため、それらをささっと紹介しつつ「3.6 ブール値の名前」「3.7 ユーザの期待に合わせる」の内容を取り上げて、もう少し分かりやすくなるようにブレイクダウンします!
リーダブルコード「3.1 ~ 3.5」の具体例
filterをかけたら選択される?除外される?
DBへの問い合わせ、C#のLINQ、JavaのStreamAPIなどでデータをフィルタリングする場合、素直に「filter」と命名するのは避けましょう。 何故なら、フィルタリングは「手段」だからです。
なぜこのデータをフィルタリングしたのでしょうか? 特定のデータが欲しかったから?特定のデータを省きたかったから?
特定のデータが欲しかったのであれば、それは沢山あるデータの中から一部を「選択」しています。つまり目的である「select(選択)」の方が明確です。
特定のデータを省きたかったのなら、それは沢山あるデータの中から一部を「除外」しています。つまり目的である「exclude(除外)」の方が明確です。
限界値(境界値)にはminとmaxを利用する
限界値には「min(minimum)」「max(maximum)」を利用することで「最小のhoge」、「最大のfuga」と最小値、最大値が含まれることを表現できます。
限界値が含まれるのか?含まれないのか?を取り違えて起こす「off-by-oneエラー」は本当にあるあるの不具合です。
この「off-by-oneエラー」を防止するためにも「min」「max」を利用して値が含まれることを明確に表現しましょう。境界値テストも大事です!忘れずに行いましょうね!
範囲を指定する場合の工夫
○○以上、××以下
範囲を指定したい場合に「○○以上、××以下」を表現したい場合は「first」「last」を使用しましょう。
first ↓ 最初は ここ |
last ↓ 最後は ここ |
||||||
---|---|---|---|---|---|---|---|
株 | 式 | 会 | 社 | こ | だ | わ | り |
「first(最初)、last(最後)」を利用することで「最初と最後が含まれる」つまり「以上、以下」であることが表現できます。
○○以上、××未満
終点を含めたくない場合の「○○以上、××未満」を表現したい場合は「begin」「end」を使用しましょう。
begin ↓ 開始 |
end ↓ 終了 |
||||||
---|---|---|---|---|---|---|---|
( | 株 | ) | こ | だ | わ | り |
ファイルが終了する文字コードのことを「End Of File(略してEOF)」と言います。EOFに到達することでファイルから文字列の読み込みが終了します。そして、ファイルから読み込んだ文字列はEOF直前の文字までになります。
このようにITでは「End」で「Endの直前までが対象」と解釈することが多いです。そのため「begin」「end」を利用することで「○○以上、××未満」であることが表現できます。
3.6 ブール値の名前
ブール値に名前を付けるときは「trueの意味が明確となる名前」を付けましょう。
変数名、引数名
以下の変数名は、trueとなったときの意味が明確ではありません。
boolean readPassword;
- パスワードを読み込む必要がある
- 既にパスワードを読み込んでいる
どちらを表しているのか判断に悩むため、実際に変数が利用されているコードを読む必要があります。
- パスワードを読み込む必要があることを表したい:「needPassword」
- 既にパスワードを読み込んでいて認証を行う:「isAuthenticated」
とすると、trueとなった時の意味が明確になります。
メソッド名
メソッド名にも同様のことが言えます。
public boolean validationCheck( ... ) { ... }
- trueを返したら問題が無い
- trueを返したらバリデーションエラーが起きている
どちらであるか判断に悩みます。そもそも「validationCheck」という名前も何をチェックしているのか分かりません。
メールアドレスのバリデーションチェックを行っているのであれば「isMailAddress()」とすれば、trueが返ってきたらメールアドレスとして問題ないと分かります。
boolean型に利用する接頭語一覧
よく見かけるboolean型の接頭語をまとめてみました。
# | 接頭語 | 意味 | 例 | trueの場合 | falseの場合 |
---|---|---|---|---|---|
1 | is | 状態 | isDelete | 削除されている | 削除されていない |
2 | can | 可能、不可能 | canDelete | 削除できる | 削除できない |
3 | has | ある、ない | hasLicence | 免許を持っている | 免許を持っていない |
4 | contains | 含む、含まない | containsKey | Keyが含まれる | Keyが含まれない |
5 | shoud | 必要、不要 | shoudPassword | パスワードが必要 | パスワードが不要 |
6 | need | 必要、不要 | needPassword | パスワードが必要 | パスワードが不要 |
7 | use | 利用する、利用しない | useSsl | SSLを利用する | SSLを利用しない |
8 | exists | 存在する、存在しない | userExists | ユーザが存在する | ユーザが存在しない |
3.7 ユーザの期待に合わせる
ITに根ずく文化が原因で自然とコードに期待を持ってしまう場合があります。この期待が原因で誤解を招くことがあります。
このような先入観を取り除くのは非常に難しいです。そのため、無理に頑張らずに受け流してしまいましょう!
頑張らずに「誤解を招いてしまう場合は名前を変えて対応」です!
get****()
getで始まるメソッドはメンバの値を返すだけの「軽量なメンバアクセスメソッド」をイメージします。そのため、getメソッドが重たい処理をしていると思いません。 値を取得する際にコストが重い処理を行う場合は「get」を使わずに「compute」などに変えましょう。
また、getメソッドは値を戻り値で返す処理をイメージします。そのため、getメソッドを利用して値が変更される部分は戻り値を受け取った変数だけで、引数で入力した値が変わるとは思いません。 引数で入力した値に副作用がある場合は「get」を使うのはやめましょう。
size()
大体の言語の標準ライブラリでは「size()」の計算量が「O(1)」となるように実装されています。そのため、sizeメソッドの計算量がデータ件数に依存するとは思いません。 sizeメソッドで重い処理を行うのはやめましょう。
引数の副作用で値を返す場合はreturnしない
getメソッドの解説ともリンクしますが、メソッドが戻り値を返している場合、そのメソッドが処理を行った結果は戻り値で受け取ると考えます。
逆に、戻り値が無いメソッドの場合は、そのメソッドが何か値を変更したとき、その変更を受け取れるものは入力した引数の副作用しかありません。
そうすると、メソッド戻り値の有無によって以下の期待が生まれます。
- 戻り値があるから「引数は変更されない」
- 戻り値が無いから「引数が変更されるかもしれない」
- もし、このメソッドが内部で値を変更していたら、引数が変更されていないと呼び出し元が処理結果を受け取れない
戻り値もあるけど、引数も副作用で変更しているという場合は、上述した期待で「引数は変更されない」と思っていると不具合を作り込んでしまいます。
引数に副作用を起こしている場合、そのメソッドは戻り値が無い方が誤解が起きません。
まとめ
本記事では、リーダブルコード3章の内容を解説しました。
3章の内容も明日から即実践できるものになっています。まずはできる範囲から取り入れてみてください。
本記事の中で記載しました中でも「○○Check」は特によく見る機会が多いです。
「○○Check」はチェックした結果が「良いのか?」「悪いのか?」が分かりにくいことが多いので気を付けましょう!ほとんどの「○○Check」は「is○○」「can○○」「has○○」で解決すると思います!