around
ProgressMonitorが嫌で、何かいい方法はないか画策中。
↓を毎回書くのがだるいし、なによりメソッドシグネチャにProgressMonitorが入るというのが移植性を下げる原因になってると思う。
// caller public void caller(IProgressMonitor monitor) { ... Hoge hoge = this.callee(new SubProgressMonitor(100, ...), ...); ... } // callee public Hoge callee(IProgressMonitor monitor, ...) { monitor.beginTask("task", 100); try { ... } finally { monitor.done(); } }
問題点を挙げてみると
- 常にIProgressMonitorを持ち歩く
- calleeは開始と終了を明示的に書く
進捗管理は、極端な話では非機能用件で、インターフェース上に出現するのはあまりよいことではないと思います。コールスタック上にメタ情報として記憶していればいいだけで、通常のシンタックスで囲うとするほうが違和感が。
そういうわけで、こういうのはどうだろ?
public void caller() { ... Hoge hoge = Monitor.sub(this, 100).callee(...); } @Monitoring("task") @TaskSize(100) // 動的に決める時はまた別の方法で… public Hoge callee() { ... }
コールに使用するオブジェクトをMonitor.subメソッドで就職して、後は普通に呼び出す感じ。元のプログラムでは呼び出されたメソッドがProgressMonitorを管理しなきゃいけないけど、制御の逆転?をして「メソッドの呼び出し」という呼び出し元と呼び出された側の中間地点で処理する。
ちなみに、Monitorの実装(?)はこんな感じ。
public class Monitor { public static <T> T sub(T target, int size) { return target; } }
実際には、プログラム変換を使ってAroundで引っ掛けるプロクシを作るのと同じような感じに変換する。スレッドを越えて動かす(コールシーケンスが切れる)場合はもうひと手間必要かも。
実はこの方法、Javaの言語レベルで似たようなコンセプト持っているものがあります。
1: 呼び出し元が責任を持つ
public void caller() { synchronized(this) { callee(); } } public void callee() { ... }
2: 呼び出された側が責任を持つ
public void caller() { callee(); } public void callee() { synchronized(this) { ... } }
3: その中間
public void caller() { callee(); } public synchronized void callee() { ... }
という感じで、synchronizeはJDK5でコンカレント系のAPIとしても登場していますが、文法としてもすでに整備されていました。
同じような方法で、トランザクションを使うときにもいいかも。
public void hoge() throws Exception { Transaction.start(session).action(); }
みたいな。
変換後はこうなる。
public void hoge() Throws Exception { Transaction t = new Transaction(session); try { session.action(); } catch (Exception e) { t.rollback(); throw e; } finally { t.commit(); } }
この程度ならAOP使ったほうがいいですね。