# 命名 ## 定義 命名とは、変数・メソッドなどのエンティティに名前を与えることで、読み手の頭の中に対象の性質についての正確なイメージを作り出す設計行為である。Ousterhout は、名前選びをソフトウェア設計の中で過小評価されがちな一側面と位置づけたうえで、良い名前は一種の文書化であり、他の文書化の必要を減らし誤りの検出を容易にする一方、悪い名前は曖昧さと誤解を生みバグの原因になると述べる。名前は一種の抽象であり、対象の最も重要な側面に焦点を当てつつ重要でない詳細を省略するという点で、他の抽象化の形態と同じ性質を持つ。名前の選択は複雑性が漸進的(incremental)であるという原則の一例であり、1 つの変数への平凡な名前がシステム全体に与える影響はわずかでも、システムには何千もの変数があるため、その総和は複雑性と保守性に大きな影響を与える。(Source: [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 14 Choosing Names]] 冒頭, §14.2) ## 著者自身の失敗談: Sprite OS の `block` バグ Ousterhout が本書で自らの失敗を最も具体的に語る箇所の一つが、1980 年代末から 1990 年代初頭にかけて開発した分散オペレーティングシステム Sprite のファイルシステムで経験したバグである。ファイルが時折データを失う不具合があり、1 つのデータブロックが理由もなく全ゼロになる現象が発生した。頻度が低く再現が難しいため大学院生数名が調査を試みたが解決できず、著者自身が最終的に 6 か月をかけて突き止めた。原因は単純で、ファイルシステムのコードが `block` という 1 つの変数名を、あるところではディスク上の物理ブロック番号、別のところではファイル内の論理ブロック番号という 2 つの異なる意味で使っていたことだった。あるコード箇所で論理ブロック番号を保持する `block` 変数が誤って物理ブロック番号を要求する文脈に渡され、無関係なディスク上のブロックがゼロで上書きされていた。著者を含む複数人がこのコードを繰り返し読んだが、`block` という名前を見るたびに反射的に「物理ブロック番号のはずだ」と思い込んでしまい、長期間気づけなかった。バグの発生箇所を突き止めるには長時間の計装(instrumentation)が必要で、名前が作り出した思い込み(mental block)を外して値の出所を確認できるようになるまでこれほどの時間を要した。`fileBlock` と `diskBlock` のように区別された名前を使っていれば、この誤用は起きなかっただろうと著者は結論づける。(Source: [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 14 Choosing Names]] §14.1) この事例が示す教訓は、`block` という名前自体は物理ブロックにもファイル内ブロックにも「そこそこ近い(reasonably close)」名前であり、決してひどい名前ではなかったという点である。多くの開発者は名前選びに時間をかけず、対象にそこそこ近ければ最初に思いついた名前をそのまま採用するが、著者はこの経験から「そこそこ近い」名前で妥協すべきではなく、精密で曖昧さがなく直感的な優れた名前を選ぶために少し余分な時間をかけるべきだと主張する。この投資はすぐに元が取れ、経験を積むほど良い名前を素早く選べるようになるという。(Source: [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 14 Choosing Names]] §14.1) ## イメージを作る(§14.2) 命名の目標は、読み手の頭の中に対象の性質についての正確なイメージを作ることである。ある名前を検討するときは「もしこの名前を、宣言も文書も利用コードも見ずに単独で目にしたら、読み手はどれだけ正確にその対象を推測できるか。もっと明確な像を描く別の名前はないか」と自問すべきである。1 つの名前に詰め込める情報には限界があり、2〜3 語を超えると名前は扱いにくくなるため、対象の最も重要な側面をわずかな語で捉えることが命名の課題になる。名前は抽象の一形態であり、複雑な対象について考える単純化された方法を提供する点で、他の抽象化の形態と同じ原理に従う。(Source: [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 14 Choosing Names]] §14.2) ## 名前は正確であるべき(§14.3) 良い名前が持つ第一の性質は精度(precision)である。名前に関する最も多い問題は、汎用的すぎる・曖昧すぎることであり、読み手は名前が指すものを推測するしかなくなる。`IndexletManager::getCount()` という例では、`count` が何のカウントかを示しておらず、`getActiveIndexlets` や `numIndexlets` のようなより精密な名前であれば読み手は文書を見なくても挙動を推測できる。学生プロジェクトから採られた実例も複数示される。文字位置を `x`・`y` で表した例は画面上のピクセル座標とも読めるため `charIndex`・`lineIndex` の方が適切であり、カーソルの点滅状態を表す真偽値 `blinkStatus` は「status」が真偽値に対して曖昧すぎるため `cursorVisible`(true が「カーソルが見えている」を意味する)の方が優れる。真偽変数の名前は原則として述語(predicate)にすべきである。特別な意味を持つ値の名前 `VOTED_FOR_SENTINEL_VALUE` は特別であることは示すがその意味を示さないため `NOT_YET_VOTED` の方が良く、戻り値のないメソッド内の `result` という変数名は戻り値であるかのような誤った印象を与え、何が計算された値かという情報をほとんど与えない。(Source: [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 14 Choosing Names]] §14.3) 精密さの原則には例外もある。ループの反復変数が数行の範囲にしか及ばず、変数の使用範囲全体を一目で見渡せるなら、`i` や `j` のような汎用的な短い名前で十分であり、コードを見れば意味は明白になる。逆に、ループが長くて全体を見渡せない場合や反復変数の意味が読み取りにくい場合は、より説明的な名前が必要になる。名前が具体的すぎる場合の弊害もあり、テキスト範囲を削除するメソッド `delete(Range selection)` の引数名 `selection` は、常にユーザーインターフェース上で選択された範囲だけに使われるかのように誤解させるため、より汎用的な `range` の方が適切である。(Source: [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 14 Choosing Names]] §14.3) > [!warning] レッドフラグ: Vague Name(曖昧な名前) > 変数やメソッドの名前が多くの異なるものを指しうるほど広い場合、その名前は開発者に十分な情報を伝えず、対象の実体が誤用されやすくなる。 > [!warning] レッドフラグ: Hard to Pick Name(名前を選びにくい) > 対象について明確な像を作る単純な名前を見つけるのが難しいなら、それはその対象がクリーンな設計になっていないことの兆候である。 ある変数に対して精密・直感的で長すぎない名前を見つけるのが難しいこと自体が、その変数が明確な定義や目的を持っていないことを示すレッドフラグとして扱われる。この場合、単一の変数で複数の対象を表そうとしていないかなど別の分解(factoring)を検討すべきであり、複数の変数に表現を分離することでそれぞれの定義がより単純になることがある。著者は、良い名前を選ぶ過程そのものが設計上の弱点を洗い出すことで設計を改善しうると述べる。(Source: [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 14 Choosing Names]] §14.3) ## 名前を一貫して使う(§14.4) 良い名前が持つ第二の性質は一貫性(consistency)である。一貫性には 3 つの要件がある。第一に、ある目的には常に共通の名前を使うこと。第二に、その共通の名前を他の目的には使わないこと。第三に、その目的が十分に狭く定義されていて、その名前を持つすべての変数が同じ振る舞いをすること。冒頭の `block` バグはこの第三の要件への違反であり、ファイルシステムがファイルブロックとディスクブロックという 2 つの異なる振る舞いを持つ対象の両方に `block` を使ったことが、変数の意味に関する誤った思い込みを生みバグにつながった。一貫した命名は共通クラスの再利用と同じ理屈で認知負荷を下げる。あるコンテキストで名前の意味を一度学習すれば、別のコンテキストでその名前を見たときに同じ知識を再利用し即座に仮定を立てられる。同種の変数が複数必要な場合(コピー元とコピー先の 2 つのブロック番号など)は、共通の名前に `srcFileBlock`・`dstFileBlock` のような接頭辞を付けて区別する。ループ変数についても、外側のループには常に `i`、ネストしたループには常に `j` を使うといった一貫性が、読み手が名前を見た瞬間に安全な仮定を立てることを助ける。(Source: [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 14 Choosing Names]] §14.4) なお本書 §14.4 のこの一貫性の議論は、対象を「命名」という個別の実践に限定したものであり、一貫性そのものをより広い設計原則として論じる範囲は第 17 章(Consistency)が別途扱う。 ## 名前の長さと明確さのトレードオフ: Go 流との対比(§14.5) Go 言語の開発者の一部(Andrew Gerrand)は著者とは異なる立場を取り、「長い名前はコードが何をしているかを分かりにくくする」として単一文字に近い短い名前を推奨する。著者はこの立場に部分的に反論し、`RuneCount` の例で `n` より `count` の方がわずかに良い手がかりを与えると述べつつも、Go コミュニティが同じ短い名前(`ch`・`d` など)を複数の異なる対象に使い回す慣行については、`block` の例と同様に混乱や誤りを招きやすいとして距離を置く。著者は最終的に、読みやすさを決めるのは書き手ではなく読み手であるべきだという立場を取り、短い名前で読者から「分かりにくい」という苦情が出るなら長い名前を検討すべきであり、逆も同様だと述べる。Gerrand の「名前の宣言と使用箇所の距離が大きいほど、名前は長くすべきである」という指摘には同意を示し、これは §14.3 で述べたループ変数 `i`・`j` の議論(使用範囲が狭ければ短い名前でよい)と同じ原則の言い換えだとする。(Source: [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 14 Choosing Names]] §14.5) ## 横断的知見 - 第 18 章(Code Should be Obvious)は、コードを明白にする最も重要な 2 技法の第一として本章の良い命名を明示的に挙げ、「名前が精密で意味を持てば読み手はコードを読み込んで意味を推測する必要がなくなる」と述べる。すなわち第 14 章が単独の名前の質(精度・一貫性)を論じるのに対し、第 18 章はその効果を「明白さ(obviousness)」というコード全体の性質の構成要素として位置づけ直す。第 14 章の Vague Name レッドフラグと第 18 章の Nonobvious Code レッドフラグは、前者が名前という部品レベルの症状、後者はコード全体という完成品レベルの症状という関係にあり、良い命名の不在は不明瞭なコードの原因の一つとして接続される。(Source: [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 14 Choosing Names]] §14.3, [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 18 Code Should be Obvious]] §18.1) - 第 13 章(コメント設計)は、変数宣言を文書化する低水準コメントについて「名前と型だけでは精度が足りず、単位・境界条件が両端を含むか否か・null が許される場合の意味・リソースの解放責任者・常に成り立つ不変条件といった情報をコメントで補う必要がある」と述べる。これは、本章が名前 1 つに詰め込める情報量に上限がある(2〜3 語を超えると扱いにくくなる、§14.2)と述べたことの裏返しであり、名前だけでは伝えきれない精度をコメントが引き継ぐという役割分担を示す。すなわち命名とコメントは競合する手段ではなく、名前が対象の大枠のイメージを作り、コメントがそれでも残る詳細を補うという分業関係にある。(Source: [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 14 Choosing Names]] §14.2, [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 13 Comments Should Describe Things that Aren't Obvious from the Code]] §13.3) ## 未解決の問い - 「名前をつけるのが難しいなら、それは設計が悪い兆候である」というレッドフラグ Hard to Pick Name(§14.3)は、第 11 章の design-it-twice(重要な設計判断ごとに根本的に異なる複数案を検討する手法)や第 4 章の深いモジュール(単純なインターフェースの背後に強力な機能を隠すモジュール)とどのような具体的な対応関係にあるかは、第 14 章の記述だけからは明確でない。名前が選びにくいと感じた時点でどのように design-it-twice の手順に接続すべきかは今後の検討課題である。 - 第 18 章は良い命名を「明白さ」を構成する 2 大技法の 1 つとして挙げるが、命名だけで解消できない不明瞭さ(イベント駆動プログラミングの間接性、汎用コンテナの使用など第 18 章 §18.2 で挙げられる要因)との境界線、すなわちどこまでが命名の改善で解決でき、どこからが設計そのものの変更を要するかは、本章の範囲では論じられていない。 - Go 流の短い名前の是非について著者は「読みやすさは読者が決める」という運用上の基準を示すにとどまり、短い名前と長い名前のどちらを既定にすべきかについての一般的な指針(チーム規約としてどう定めるか)は示されていない。 ## 関連 - 章: [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 14 Choosing Names]] - 関連章(命名と接続する他章。本ページでは直接論じない): [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 4 Modules Should Be Deep]] / [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 11 Design it Twice]] / [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 13 Comments Should Describe Things that Aren't Obvious from the Code]] / [[@2018__YaknyamPress__A Philosophy of Software Design - Chapter 18 Code Should be Obvious]] - 実体: [[wiki/entities/A Philosophy of Software Design|A Philosophy of Software Design]] / [[John Ousterhout]] ## 出典 - John Ousterhout, *A Philosophy of Software Design*, Yaknyam Press, 2018, Chapter 14.