uupaa-color.js の リライト

uupaa-color.js は例によりuupaa.js の一部を切り出し、単体で動作可能な形にリパックしたスクリプトです。

一見あまり意味のない(ニーズが無い)ように見えるスピンオフなんですが、

  • 単体リリースしておけば、Core(uupaa.js)に入れたままだと気が付きにくい問題が見つかる可能性が増える
  • ソースが短いのでデバッグが簡単になる(Firebugがサクサク動く)。
  • Core(uupaa.js)に圧縮した状態のコードを載せると「コード読めねぇぞボケェ」とか言われちゃうだろうから、スピンオフしたコードは、ヒューマンリーダブルなコードにしておくことで、その手の苦情に対する担保的な役割も果たしてくれそう。

という狙いがあります。


前置きここまで。

今日は、uupaa-color.js を以下のように書き直しました。

根っこから書き直しました。

改善前(ver1.0)の使用例から抜粋

// チョコレート色("chocolate")を基本に、明度(V)を+20〜-20した色を表示する
<div id="id0">base</div>
<div id="id1">down20</div>
<div id="id2">down10</div>
<div id="id3">base</div>
<div id="id4">up10</div>
<div id="id5">up20</div>

window.onload = function() {
  var ns = uu.color;
  var rgba    = ns.hash("chocolate"); // base color
  var hsva    = ns.rgba2hsva(rgba);
  var v20down = ns.ratio(hsva, 0, 0, -20);
  var v10down = ns.ratio(hsva, 0, 0, -10);
  var v10up   = ns.ratio(hsva, 0, 0, 10);
  var v20up   = ns.ratio(hsva, 0, 0, 20);
  var rgba20down = ns.hsva2rgba(v20down);
  var rgba10down = ns.hsva2rgba(v10down);
  var rgba10up   = ns.hsva2rgba(v10up);
  var rgba20up   = ns.hsva2rgba(v20up);

  document.getElementById("id0").style.backgroundColor = ns.rgb(rgba);

  document.getElementById("id1").style.backgroundColor = ns.rgb(rgba20down);
  document.getElementById("id2").style.backgroundColor = ns.rgb(rgba10down);
  document.getElementById("id3").style.backgroundColor = ns.rgb(rgba);
  document.getElementById("id4").style.backgroundColor = ns.rgb(rgba10up);
  document.getElementById("id5").style.backgroundColor = ns.rgb(rgba20up);
}

途中経過を見せるためにわざわざ変数に代入しているので、ちょっと冗長な書き方なんですが、本質的には手続き型のアプローチを取ってます。

ver2.0だとこうなります。

uu.color("chocolate").lock().ratio(0, 0, -20).ratio(0, 0, -10).copy().ratio(0, 0, 10).ratio(0, 0, 20).unlock().hexEach(function(v, i) {
  document.getElementById("id" + i).style.backgroundColor = v;
});

STL::stack とか STL::queue とか をイメージしてもらえると、わかりやすいと思います。

push()で色を追加, pop()で削除, copy(n)でコピー, ref(n)で参照, clear()できれいさっぱり。
lock()で色を束縛, unlock()で束縛を解除。
ratio()で色相,彩度,明度を調整した色を追加, complementary()で補色を追加。
forEach(), hexEach(), rgbaEach() でスタックをイテレート。
size()で現在のスタック数(1〜)を返す。スタックは常に1以上存在するつくりなのでempty()は無し。
top()でトップのスタックにアクセス。
hex(n)は"#FFFFFF"を rgb(n)は"rgb(,,)"を, rgba(n)は"rgba(,,,)"を, hsva(n)は"hsva(,,,)"を返す。
toString()はrgb()のエリアス。

lock()は、その時点のトップスタックをcopy,ref,ratio,complementaryで参照するように指示(束縛)するメソッドです(例では"chocolate"を束縛している)
ratio()が参照する値は(通常は)その時点のトップスタックなのですが、lockされているため("chocolate")に対して明度を調整した値を作成しスタックに積むようになります。

見慣れない hexEach は vにhex("#FFFFFF")をよこすタイプのイテレータ(forEachの亜種)です。

JavaScriptなんで演算子オーバーロード等はできませんが、できるかぎりSTLチックなI/Fにしてみました。

反省会

  • jQueryでメソッドチェーンをはじめて知った人は「jQuery独特の繋げる書き方がすげー」とか言うけどさ。ジェネリックな世界(STLやBoost)ではメソッドチェーンは空気みたいなもんだから、その辺を理解しておかないと赤っ恥かくこともあるかもね。
  • 今回メソッドチェーンを使ったのは、性能に影響が出ないようにうまく纏めることができると判断したから。
    • しつこいけど、何でもかんでもメソッドチェーンさせちゃうとだめになるんよ。
    • 手続き的な処理が仕様変更に対し最もフレキシブルな(つぶしが利く)ことは昔から変わらないから。
      • アホみたいにOOPデザパタを導入するのはやめて、レガシーな場所とのすり合わせも見極めないと。
      • せっかく学習したんだから、実戦で使いたくなる気持ちもわかるけどさ。OOPありきで設計するとちゃぶ台返されて燃え上がった時にどうしようもなくなるよ?
  • 「失敗も経験のうちだから」とか言っちゃう会社とは、仕事しないほうがいいと思う。

最後のほうは誰に宛てたメッセージかわからなくなりましたが、まぁがんばって欲しいもんです。