僕が設計の逆流問題と呼んでいる概念の話をしたい。
僕がコードを書いていて一番疲れることの一つだ。
一般に下レイヤーのコードが上レイヤーのコードに感知しないのがいい設計ということになっている。
例えば、WebAPIを開発しているとしよう。APIのハンドラ内でDBからデータを取得し、それを加工してレスポンスするとする。
Web APIを書くディレクトリの中にDBアクセスのコードを書くと汚くなるので、DBアクセスは別のディレクトリに書いてインポートすることにした。
こんな感じのコードになる。
/db/user.dart
User getUser(String userID) {
...
}
import “../db/users.dart” as db;
HttpResponse getUser(HttpRequest httpRequest) {
return new HttpResponse()..body = db.getUser(httpRequest.param["user_id"]);
}
このとき、/db/users.dartから書くか、/web_api/get_users.dartから書くか、それが問題だという話だ。
善悪の観点から言えばdbアクセサを書いてからWebAPIハンドラを書くのが正しい。
なぜなら依存関係を1方向にし、依存関係の後ろ側から書いていく方が自然だからだ。
しかし、ここで設計の逆流問題が起こる。
上レイヤーで何をするかわからないと下レイヤーがどんな機能を公開するか決められないのだ。
例えば/db/users.dartにはどんな処理が必要か?
- UserIDでUserを一件取得する
- 30才以上のユーザーを取得する
- 商品1を購入したユーザーを取得する
などなど。
これは/web_api/**のすべてのコードがわからないとと決めようがない。
では/web_api/から書いていくべきか?
そうでもない。
上レイヤーから書いていくと終始コンパイルエラーがでて目障りだ。
しかも、コード補完やリファクタリングなどIDEの機能が使えなくなる。
ここで二つの方法が考えられる
一つは深さ優先探索だ。
WebAPIハンドラを書きながら必要に応じてDBアクセサを書いていく。
些細なことならこれが一番楽だ。
しかし、あっちこっちいろんなファイルを触ることになるので、僕のように脳内ワーキングメモリが乏しい人は「あれ? 今何してたんだっけ」状態になりやすいのが難点だ。
そうでなくとも、要領が悪い方法なのは確かだ。
もう一つ、事前リストアップ作戦がある。
先に脳内でWebAPIハンドラのコードを書き、必要な下層の機能をメモ帳に書いていく。
この方法はかなり時間短縮になる。特に大きい作業ではかなり効いてくる。
しかし、まず疲れる。
第二に不正確になりがちである。
第三に、そもそもプログラミング言語が一番得意そうな領域のことをわざわざメモ帳に自然言語で書くせいで消耗するのは筋が悪くないか?
結局どうしようもないというのが僕の結論だ。
そこで僕は考えた。要するにリストアップする専用の書式か何かがあればいいのではないか?
単にリストアップする書式よりも、下層が上層からどのように使われるかを擬似コードで書いていけばその使われ方を集約してくれるものがあればいい。
それなら、プログラム言語をそのまま使った方がいい。
そして至った暫定解がこれだ。
まず、WebAPIハンドラを先に書く。
バックグラウンドプロセスがそのファイルを監視し、そのコードのコンパイルが通るようにinterface文を自動生成していく。
例えば、WebAPIハンドラに
var user = db.users.getUser(httpRequest.useID)
というコードがあれば
abstract class DB {
SomeInterface users;
}
abstract class SomeInterface {
dynamic getUser(String arg1);
}
というinterfaceを自動で生成すればいい。
すると、まずコンパイルエラーが消える。もちろん、本当に間違った処理を書いていたらエラーを消して欲しいので、例えば「db.」で始まるステートメントとその子どもっぽいものだけ自動生成すればい。
さらに、一回使ったメンバーは次回からIDEが自動補完してくれる。リファクタリングもIDEから直接実行可能だ。
上レイヤーを書き終わったら、interfaceを実装するように下層を書いていけばいい。
僕は今、具体的な実装方法やより詳細なかた情報をバックグラウンドプロセスに伝える手続きなどを色々考えてる。