2013-02-20 Wed 08:30
get_user()の何がいかんのか
先のエントリで以下のように述べた伏線の回収エントリとなります。
そして、get_user()という名前はサンプルコードにはありがちだが、この名前も闇が深いと思っている。これについてはまた別途取り上げたい。
このメソッドを構成する単語であるget, userともに問題があると考えており、それぞれについて見ていきます。
Userとは具体的に何か
これはよくある話で、まず自問すべきは「そのUserの定義を教えてください」である。
多くの業務システムではユーザ(アクタ)が複数存在しており、単に「ユーザ」と言った場合に指す対象が文脈によって異なることが多い。
これに対しては、Employee, Staff, Administrator, Visitor, Sponsorなど別の具体的な単語に置き換えられないかを検討することになる。
「ユーザ」が一意なシステムであればUserと名付けるのも妥当だろう(SNSなどが該当するだろうか)。
userについては、より具象化した名前が見付からないかよく検討しましょう、という話で簡単に終わる。
get_XXX()という名前
このgetという名前についてはここ数年に渡って思うところがあり、もやもやしていたが、この話題を通してある程度は自分のスタンスが見えてきた。
個人的な経験から、近年のプログラム言語はメソッドとプロパティ(メンバ変数)の境界線が曖昧になる方向に進んでいると感じている。
具体的には、PerlでMoose系モジュールによるlazyとdefaultを使ったアクセサと初期値の定義や、Pythonの@propertyがそう感じるに至った体験である。また、JavaScriptでもクロージャを使うことで似たような感覚に陥ることがある。
ここから生まれた疑問が「メソッドや関数への参照を変数に格納可能な言語において、その内容が現物の値そのものなのか、何らかの処理の結果なのかを区別することに意味はあるのか」というものだ。
書籍『リーダブルコード』では、
多くのプログラマは、getで始まるメソッドはメンバの値を返すだけのアクセサ」であるという規約に慣れ親しんでいる。この規約を守らなければ誤解を招く可能性がある。
として、get*()にコストの高い処理を組み込むことを戒め、
コストの高さが事前にわかるように、このメソッドはcoputeMean()などの名前に変えるべきだろう(あるいは、コストの高くない実装に変えるべきだろう)。
と書いている。この論には大筋では同意なのだが、「そもそもgetを付ける必然性はあるのか」という疑念がついて回る。
setterについては言語仕様も含めて一概には言えないが、少なくともgetterはプロパティへのアクセスと区別する必要はないとの思いが日に日に強くなっている。
また、呼び出しのコストについては名前の如何に関わらず、呼び出し元がそこを意識するべきではないとも思う。
少し理想論も入ってくるが、外部に公開しているインタフェースは呼び出し元がその内部の実装を意識することなく利用されるべきであると考えている。非機能要件が機能を制限すべきでない、とも言い換えられる。
現実的には呼び出しコストを完全に無視できないケースは多々あるが、それが命名規則に影響を与えることには違和感がある。
例えば、キャッシュを導入すればそれまで毎回大きなコストがかかっていた処理が、最初の1回だけ高コストで、その後は軽い処理になる。
その計算をアクセサが呼ばれた瞬間でなく、オブジェクトが生成されたタイミングで行えば、メソッドそのものは重い処理ではなくなる。
更には、事前にその集計結果をKVS等に格納することになれば、オブジェクト生成も含めて呼び出しコストの問題は無視できるレベルになるだろう。
このような対策が考えられる問題に対して、命名が影響を受けることには疑問がある。
コストの問題は命名とは別のものとして、別途計測やチューニングの対象とすべきではないか。高コストであっても呼び出し頻度が低ければ問題にならないことも多いだろうし、頻度が高いのであれば、名前で警告するよりもコスト軽減に目を向けた方が実りが多いように思う。
3行まとめ
- 抽象化されたサンプルの名前に引きずられるな
- アクセサとプロパティの違いは明確にしなくていい
- 命名と処理コストは別問題として扱え