メソッドを呼び出してその値を返す必要があるプロセスがあります。ただし、状況に応じて、このプロセスで呼び出す必要があるメソッドがいくつかあります。メソッドとその引数を (Python のように) プロセスに渡すことができれば、これは問題ありません。ただし、Javaでこれを行う方法はわかりません。
これが具体的な例です。(この例では Apache ZooKeeper を使用していますが、例を理解するために ZooKeeper について何も知る必要はありません。)
ZooKeeper オブジェクトには、ネットワークがダウンした場合に失敗するいくつかのメソッドがあります。この場合、私は常にメソッドを再試行したいと考えています。これを簡単にするために、ZooKeeper クラスを継承する「BetterZooKeeper」クラスを作成し、そのすべてのメソッドは失敗時に自動的に再試行します。
コードは次のようになります。
public class BetterZooKeeper extends ZooKeeper {
private void waitForReconnect() {
// logic
}
@Override
public Stat exists(String path, Watcher watcher) {
while (true) {
try {
return super.exists(path, watcher);
} catch (KeeperException e) {
// We will retry.
}
waitForReconnect();
}
}
@Override
public byte[] getData(String path, boolean watch, Stat stat) {
while (true) {
try {
return super.getData(path, watch, stat);
} catch (KeeperException e) {
// We will retry.
}
waitForReconnect();
}
}
@Override
public void delete(String path, int version) {
while (true) {
try {
super.delete(path, version);
return;
} catch (KeeperException e) {
// We will retry.
}
waitForReconnect();
}
}
}
(実際のプログラムには、簡単にするために例から取り出したより多くのロジックと多くのメソッドがあります。)
同じ再試行ロジックを使用していることがわかりますが、引数、メソッド呼び出し、および戻り値の型はすべてメソッドごとに異なります。
コードの重複を排除するために私がしたことは次のとおりです。
public class BetterZooKeeper extends ZooKeeper {
private void waitForReconnect() {
// logic
}
@Override
public Stat exists(final String path, final Watcher watcher) {
return new RetryableZooKeeperAction<Stat>() {
@Override
public Stat action() {
return BetterZooKeeper.super.exists(path, watcher);
}
}.run();
}
@Override
public byte[] getData(final String path, final boolean watch, final Stat stat) {
return new RetryableZooKeeperAction<byte[]>() {
@Override
public byte[] action() {
return BetterZooKeeper.super.getData(path, watch, stat);
}
}.run();
}
@Override
public void delete(final String path, final int version) {
new RetryableZooKeeperAction<Object>() {
@Override
public Object action() {
BetterZooKeeper.super.delete(path, version);
return null;
}
}.run();
return;
}
private abstract class RetryableZooKeeperAction<T> {
public abstract T action();
public final T run() {
while (true) {
try {
return action();
} catch (KeeperException e) {
// We will retry.
}
waitForReconnect();
}
}
}
}
RetryableZooKeeperAction は、関数の戻り値の型でパラメーター化されます。run() メソッドは再試行ロジックを保持し、action() メソッドは実行する必要がある ZooKeeper メソッドのプレースホルダーです。BetterZooKeeper の各パブリック メソッドは、RetryableZooKeeperAction 内部クラスのサブクラスである匿名の内部クラスをインスタンス化し、action() メソッドをオーバーライドします。ローカル変数は (奇妙なことに) 暗黙的に action() メソッドに渡されます。これは、それらが final であるため可能です。
最終的に、このアプローチは機能し、再試行ロジックの重複を排除します。ただし、これには 2 つの大きな欠点があります。(1) メソッドが呼び出されるたびに新しいオブジェクトが作成されること、(2) 見苦しく、読みにくいことです。また、無効な戻り値を持つ「削除」メソッドを回避する必要がありました。
だから、ここに私の質問があります.Javaでこれを行うより良い方法はありますか? これはまったく珍しいタスクではありません。また、他の言語 (Python など) では、メソッドを渡すことができるようにすることで簡単に処理できます。リフレクションを通じてこれを行う方法があるのではないかと思いますが、頭を包むことができませんでした。