getter と setter は混ぜないで

代入と参照

変数に値を設定し取得するには = を使います。

obj.name = "setter"; // obj.name に "setter" を設定する
var a = obj.name; // obj.name の値を取り出し a に代入する

すごく自然ですね。
= が左にあるか右にあるかで、コンパイラは代入(lhs)か参照かを区別しています。
# lhs: Left-Hand Side, 左辺値
# rhs: Right-Hand Side, 右辺値

いっ〜ぽすすんで♪

obj に getter と setter と呼ばれる関数を用意するとこう書けます。

obj.set("name", "setter"); // obj.name に "setter" を設定する
var a = obj.get("name"); // obj.name の値を取り出す
  • タイプ数が増えた
  • obj の中身を気にする必要がなくなった
    • obj の中身が単一の要素でもコレクションでも同じようにアクセスできるようになった
  • 関数呼び出しの分コストが増えた(遅くなる要因が増えた)

にほさがるっ♪(本題)

さらに、obj.get(keyword) と obj.set(keyword, value) の引数の数や型の違いに注目し、getter/setterを一つに纏め上げられる事に気付く人がいます。

obj.manip("name", "setter"); // 引数を二つ指定した場合は、obj.name に "setter" を設定する
var a = obj.manip("name"); // 引数を一つだけ指定した場合は、obj.name の値を取り出す


「オレ様すげぇ。便利になっちゃったぜ!」

でもね、これは、やっちゃダメなんです。

ダメな理由は、

  • 引数が増えたらどうする?
    • 拡張性を捨ててまで、そうしなければならない根拠は?
  • 問題発生時のデバッグコスト(後述)は考慮してますか?
  • 他のメソッドとバランスが取りづらくなる恐れはありませんか?
  • 引数の数や型で内部分岐が入るので、どんどん遅くなってますよね?

このような例は、jQueryなどで見られます。

これらを見て、

  • どの attr() と css() が setter で、どれが getter なのか?
  • 戻り値は jQuery なのか? それとも Hashなのか?

といった事を、一見で正しく把握できる人は少ないでしょう。

これをスマート(簡潔)な記法と受け取るか、危険な記法と受け取るかはそれぞれだと思いますが、私は「危うさ」を感じました。

なんでコレじゃだめなの?

  • jQuery(expr).setAttr(key, fn) // setter
  • jQuery(expr).setAttr(key, value) // setter
  • jQuery(expr).setAttr(properties) // setter
  • jQuery(expr).getAttr(name) または jQuery(expr).attr(name) // getter

検索コストの問題

“時々エラーがでる。どうもある要素に何かを代入しているタイミングらしい”という状況なら、/name\s+\=/ で grep すれば、該当箇所が発見できる可能性がかなりあります。

obj.name = .....

しかし、jQuery(expr).attr() のように getter と setter の区別が付かない API セットではどうでしょう?

function attrManipulate(obj, key, value) {
  return obj.attr(key, value);
}

このようにラップしても犯罪ではありません。しかしこれでは grep しても一目で何がおきているのか絶対に把握できません。この例では、全ての呼び出し元で(さらにその先まで)、「引数に何が渡されるのか?」を追跡するはめになります。

Ext.js などのように YUIjQuery を包含可能なライブラリでは、attrManipulate のようなラッパーコードを見かける機会も多いでしょう。

何がいいたいか

コードを書くのは一瞬です。保守はずっとです。
保守を考えずに ver 1 で「えいやっ」って書くから、ver 2 でデスマになるんです。

getter と setter をまぜこぜにしている jQuery(expr).attr() は、検索精度やコストよりも コード記述時のコスト( + jQueryのサイズ)を、最優先とした結果なのでしょう。

コードを書くのは(たぶん)1人が1回だけ。コードの検索は皆が何度も。どっちがコスト高になるかは明白です。

"set" の3文字をタイプする手間(0.5秒かそこら)を1人がサボることで、チーム全員が会社にお泊まりする。な〜んて事は、まともな開発経験をお持ちの皆さんなら心当たりありすぎて(ry

# 今はよくてもさ、「すいませんデバッグ手伝ってください、納品のリミットが1時間後なんですぅ」って言われてコードの中身がコレ式(呼び出し元全部追い)だったら? 文句の一つでも言いたくなるでしょう。


所詮 JavaScript なんだから、話半分でOK?
それっぽい理由が attrManipulate() の、一つだけだと説得力に欠ける?
う〜ん、そう思える人は、そのままの君でいいと思うよ。

反省会

  • 色々書いたけど、半分は自分に対する戒めだったりする。
    • 私の中で当たり前すぎる話なんだけど「自分の常識、他人の非常識」かもしれしないし、こういう面倒くさい話はあんまり書く人がいないようなので、書いてみた。
  • jQuery はバランスが秀逸なんだと思う。