java - 如何在Java中通用化方法调用(避免代码重复)

我有一个需要调用方法并返回其值的进程。但是,根据具体情况,可能需要调用几个不同的方法。如果我可以将方法及其参数传递给进程(如在python中),那么这就没有问题。但是,我不知道在java中如何做到这一点。
这里有一个具体的例子。(本例使用ApacheZooKeeper,但是您不需要了解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()方法,这是可能的,因为它们是最终变量。
最后,这种方法确实有效,并且消除了重试逻辑的重复。但是,它有两个主要缺点:(1)每次调用方法时都创建一个新对象,(2)它很难看,很难阅读。另外,我还必须解决“delete”方法,该方法具有void返回值。
所以,我的问题是:在java中有更好的方法来实现这一点吗?这并不是一个完全罕见的任务,其他语言(如python)通过允许传递方法使之更容易。我想也许有办法通过反射来做到这一点,但我还没能把我的头裹住。


最佳答案:

这看起来(至少在我看来)是“正确的”java风格(或者类似于java中的“pythonic”)的重构。您已经正确地使用了template方法模式,并且您将最终变量传递到内部匿名子类是即时的,正如内部类的设计者所期望的那样。您保留了静态类型,并且很好地使用了泛型。
使用反射的解决方案确实是可能的,但它确实牺牲了一些静态类型的精确性,并且您的代码将不得不捕获一些调用方法时出现的检查异常,从而增加了一些混乱。反省被高估了,在这里没有必要。我们倾向于在java中更多地使用它来进行检测,而不是使代码更易于阅读。如果您想干净地传递方法,请等待Java8!
另外,我不相信你的代码是不可读的。专业的java程序员应该能够阅读这篇文章。这种类型的内部类存在。也就是说,可以重构“一个步骤”,并将现在的匿名类命名为本地类(当然,在方法中是stil),这样就不会很难找到对run()的调用。除此之外,我没有真正的重构建议。

译文:来源   文章分类: java methods inner-classes anonymous-class code-duplication

相关文章:

java - Jackson定制过滤器,具有完整的POJO数据绑定

java - 如何通过Java运行我的haskell函数

java - 处理多个异常

c# - 比较带有对象的null而不是带有null的对象有什么问题

java - 如何将pdf文件存储在Google云存储中?

java - samza容器失败了

java - Spring框架中的DAO和Service层是什么?

java - 注射器层次/儿童注射器解释?

java - Java FX组合框重置问题

java - 如何使用Spring 3 MVC实现进度条?