💋

コンテキストを意識して名前をつける

2021/03/10に公開

システムを触っていると何かに名前をつける機会がたくさんあるが、その名前を見た人が意味を一意に特定できる名前をつけると良い。そしてそのためには名前が通用するコンテキストを意識するとよい、という話を文章にまとめる。

まとめ

  • 名前をつけるときはその名前がどのコンテキストで有効なのか意識するとよい
  • そのコンテキストの中でその名前が指す意味が一意に特定できること、齟齬が起きないことを意識するとよい
  • 例えば「ユーザー」の意味が3種類あるコンテキストで、単に「ユーザー」という名前をつけるべきではない

ここでいうコンテキストとは

文脈とか背景とか略されるが、この記事の中では暗黙もしくは明示的に何かの単語が具体的な何かを指すことが約束された範囲のことを指す。
例えば、特定の喫茶店に頻繁に遊びに行くグループ内の会話というコンテキストにおける「喫茶店行こうぜ」という発言の中の「喫茶店」はおそらく「よく遊びに行く近所の喫茶店」を意味する。
しかし、コンテキストが変わると「喫茶店」を単語が指す対象は変わる。

コンテキストを意識しない名付けは解釈が一意にならないことがある

「特定の喫茶店に頻繁に遊びに行くグループ内の会話」に、そのあたりの事情がよくわかっていない転校生が参加した場合、「喫茶店」の意味がずれてしまう可能性がある。
例えば以下のような状況で「放課後喫茶店に集合な」をいう約束をしたとする。

  • この会話は学校で行っている
  • 普段このグループが集まる喫茶店はタリーズである
  • 学校のすぐ隣にドトールがあり、タリーズはちょっと離れたところにある

この場合コンテキストを共有できているグループ内のメンバーはいつも利用している「タリーズ」に集まるが、転校生は「ドトール」に行ってしまうというバグが発生する可能性がある。

「放課後タリーズに集合な」という言い方をすることでバグを回避できるかもしれない。しかしこれは、「タリーズ」という言葉から想像できる店舗が一意に特定できるコンテキストでのみ通用する。
別の地域に住んでいる他校の生徒と喧嘩をするために「放課後タリーズに集合な」という言葉を使ってしまうと、各自が思い思いのタリーズに向かってしまうため喧嘩が成立しないバグが発生する可能性がある。

システムにおけるコンテキスト

例えば「アプリケーション」「サービス」「機能」「クラス」「メソッド」のようなコンテキストがある。

スコープの小さい変数のようにある程度コンテキストの範囲が明確でわかりやすいケースもあるが、大抵の場合この単語が通用するコンテキストがどの範囲か、という問題は曖昧でわかりづらい。しかし、それでも名付けの際にこの名前がどのコンテキストで通用し、そのコンテキストの中で齟齬が起きないかどうかについて考えることは、良い名付けにとって有効なやり方である、というのがこの記事の趣旨である。

以下のような機能を追加する場合について考える。

# 前提
このアプリケーションではユーザーは、ユーザーIDとパスワードを使って認証を行う。
# 追加する機能
認証が成功したら認証履歴をデータベースに登録し、過去の認証履歴を参照できるようにする。

既存のIDとパスワードによる認証は以下のように User クラスの authenticate メソッドで行っているとする。

public class User {

    /**
     * 認証に失敗した場合例外をスローする
     */
    public void authenticate(String passwordFromForm) {
        // パスワードを検証したりする処理
    }
}

ユーザーの認証履歴を記録するため、以下のクラスを作成する。

public class AuthenticationHistoryManager {
    
    public AuthenticationHistory save(String id) {
        // データベースにユーザーの認証を記録
    }
}

AuthenticationHistoryManager という名前は、コンテキストの中で意味が一意になるか?

Manager という名付けの良し悪しはともかく、認証の履歴をよしなにするクラスという意味では間違ってはいないように見える。しかし、間違っていない名前でも状況によっては認識の齟齬が起きる可能性がある。

もしこのアプリケーションがサードパーティアプリケーション向けの API を提供していて、API キーを用いてサードパーティを検証する行為も認証と呼んでいた場合のことを考える。

AuthenticationHistoryManager という名前からはユーザーの認証履歴を記録するためのクラスなのか、サードパーティアプリケーションの認証履歴を記録するためのクラスなのかを判断するのは難しい。特に規模が大きく開発の分業が進んでいて、ユーザーの認証とサードパーティアプリケーションの認証で開発チームが分かれているようなケースでは、後者のチームが AuthenticationHistoryManager の意味を勘違いしてしまう可能性はそれなりにあると思う。

例えば UserAuthenticationHistoryManager という名前をつけることでユーザーなのかサードパーティなのか、という点での齟齬を防ぐことができるかもしれない。(UserAuthentication が意味するものを一意に特定できるのであれば)

この例では、AuthenticationHistoryManager という言葉が通用する範囲はおそらく AuthenticationHistoryManager クラスを触ることができる範囲、とある程度同じになる。おそらくコードを見るのはコードに興味がある人だからだ。

例えば、ユーザーの認証とサードパーティアプリケーションの認証でモジュールが分割されていて互いのコードにまったく依存がないのであれば、ユーザーの認証を行うモジュール内の AuthenticationHistoryManager はユーザーの認証履歴をつかさどるクラスであることが判断しやすくなる。

チーム内での意思が統一できているのであれば、user パッケージ以下に存在する AuthenticationHistoryManager はユーザーに関連する処理を行うクラスである、という判断でも混乱は起きないかもしれない。

しかしモジュールが分割されていたとしても別のコンテキスト、例えばシステム全体に関するマニュアルを書く場合に認証履歴AuthenticationManager という単語を使ってしまうと、どっちの認証のことを言っているのか、という点でまた齟齬の原因になりうる。

というような意味で、名前をつけるときはその名前がどのコンテキストで有効なのか、そのコンテキストの中で齟齬が起きないのか、といったことを意識することは良い名付けにとって有効だと思っている。

まとめ

  • 名前をつけるときはその名前がどのコンテキストで有効なのか意識するとよい
  • そのコンテキストの中でその名前が指す意味が一意に特定できること、齟齬が起きないことを意識するとよい
  • 例えば「ユーザー」の意味が3種類あるコンテキストで、単に「ユーザー」という名前をつけるべきではない
GitHubで編集を提案

Discussion