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();
  }
}

問題点を挙げてみると

  1. 常にIProgressMonitorを持ち歩く
  2. 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使ったほうがいいですね。