最近投げたpull requestとかソーシャルコーディングとかリファクタリング

RubyMotionを相変わらずいじってるのだけど、最近は業務に疲れてあんまり趣味開発が捗ってない。 趣味開発しようとする前に使うgemがまだいい感じに枯れてないので不満が出るからまずそっちを直そうみたいなことを繰り返している気がする。

最近直したgemで、ibというRubyMotionでInterface Builderを使うために、app/*以下のRubyのコードをparseして、Outletとかが定義されたダミーヘッダーファイルを作ってくれる奴がある。

$ rake ib:open

というコマンドを叩くとローカルにib.xcodeprojを作ってくれて、その中にあるStubs.hとxibファイルをマウスでうにょーってoutletの連携する事ができる。

吐き出してくれるStubs.hには、OSXアプリを作る時は#import <UIKit/UIkit.h>じゃなくて、#import <Cocoa/Cocoa.h>を書いてて欲しいんだけど、なかったので付け足そうとした。

付け足そうとした時に、元のコードでは、Stubs.hをメソッドの中のヒアドキュメントで全部書いていて、拡張し続けるとクラスがデカくなってくなーと思い、tiltというgem経由でerbを導入した。 テンプレート処理を分離すると、IB::Generatorクラスがスッキリして良くなって、吐出されるファイルの整形もしやすくなった。

その後、今度は、Stubs.hに吐出されるクラスの定義の順番が何も考えられずに吐出されてエラー出たりしたいたので、親クラス・子クラス間の定義順を考慮したStubs.hを吐き出すようにpull requestを投げた。(まだマージされてない)

こういうの作る時どうすればいいんだっけかな−?依存関係ってそういえば大学でトポロジカルソートってアルゴリズム習ったし使えそうだな−と思いながら、bundlerのコード開いてみたら、Rubyの標準ライブラリにtsortの実装があることがわかり、そのまま使った。

TSortはこんな感じの無閉路有向グラフ(1は2,3に依存してて、2は3に依存している)ってをHashで定義してやると、ソート結果を配列で返してくれる。

{1=>[2, 3], 2=>[3], 3=>[], 4=>[]}.tsort  #=> [3, 2, 1, 4]

ibがapp/*以下のファイルをパースした結果のデータ構造を一回TSort用にHashに落としこんで、ソートして、それをもとにパース結果を並び替えるようにした。

それはそうとして、ibのパース結果が若干複雑で、ハッシュの配列のハッシュの配列の・・・みたいな感じになっている。

{
  'aaa.rb' => [{
      class: [['SubClass1', 'SubClass2']],
      outlets: [["greenLabel",    "UIGreenLabel"]]
      outlet_collections: [["greenLabelCollection", "UIGreenLabel"]],
      actions: [["someAction", "sender", nil]],
    }
  ],
  'bbb.rb' => [
    ...
  ],
}

こうなると、あとからコード弄る人がすごい触りづらい。機能的を足すときには、出来るだけ既存のクラスとは別クラスとして責務を分けて実装したいけど、そんな時にハッシュと配列の組み合わせの複雑なデータ構造だけでデータを表現していると、そのデータの構造を他のファイルでも滞りなく扱えるようにしなきゃいけないし、何よりちょっとデータ構造変わるとすぐ壊れそう。

PerlRubyによくあると思うけど、こういうハッシュとか配列の組み合わせでデータ構造表現するの、便利なんだけどメンテナンス性がそんなに良くない。面倒くさくてもデータ構造用のクラス定義したほうが良いなと思った。

pull requestとかソーシャルコーディングと最近はやし立てられてるけど、そのたびに、さっきみたいなライブラリ全体で使われるようなデータ構造を大幅に変えるような仕様変更が起きるのはコントリビューターたちにはとっては、理解するのにすごいコストだと思うし、そもそも仕様を変更する時は、議論したりするコストがすごく高そう(自分一人で開発してるならバンバン勝手に仕様変更しても良いかもしれないけど)。

そんなわけで、ソーシャルコーディング前提の時代では、コードを公開し続ける限り、誰かに変更され続ける可能性があるわけだし、自分のコードをソーシャルな力で成長させていきたいならば、アジャイル開発の奥義に書かれているような、変更しやすいソフトウェアってのを常日頃意思して開発していくべきだなぁと思いました。

特に単一責任の原則(SRP)は重要だと思う。