gpt4 book ai didi

java - 如何在 Java 中概括方法调用(以避免代码重复)

转载 作者:塔克拉玛干 更新时间:2023-11-02 08:56:49 26 4
gpt4 key购买 nike

我有一个进程需要调用一个方法并返回它的值。但是,根据情况,此过程可能需要调用几种不同的方法。如果我可以将方法及其参数传递给进程(就像在 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 的每个公共(public)方法都实例化了一个匿名内部类,它是 RetryableZooKeeperAction 内部类的子类,并且它重写了 action() 方法。局部变量(足够奇怪)隐式传递给 action() 方法,这是可能的,因为它们是最终的。

最后,这种方法确实有效,并且确实消除了重试逻辑的重复。但是,它有两个主要缺点:(1) 每次调用方法时都会创建一个新对象,(2) 难看且难以阅读。此外,我还必须解决具有无效返回值的“删除”方法。

那么,这是我的问题:在 Java 中有没有更好的方法来做到这一点?这不可能是一项完全不常见的任务,其他语言(如 Python)通过允许传递方法使其变得更容易。我怀疑可能有一种方法可以通过反射(reflection)来做到这一点,但我一直无法全神贯注。

最佳答案

这似乎(至少对我而言)是“正确的”Java 式(或 Java 中“Pythonic”的任何类似物)重构。您已经正确地使用了模板方法模式,并且将 final 变量传递到内部匿名子类中是正确的,正如内部类的设计者所期望的那样。您保留了静态类型并且很好地使用了泛型。

使用反射的解决方案确实可行,但它确实牺牲了一些静态类型的细节,并且您的代码将不得不捕获一些调用方法附带的已检查异常,从而增加了一些困惑。反射被高估了,恕我直言,这里没有必要。我们倾向于在 Java 中更多地使用它来进行检测,而不是使代码更易于阅读。如果你想干净地传递方法,请等待 Java 8!

此外,我不认为您的代码不可读。专业的 Java 程序员应该能够阅读本文。内部类就是为这种类型的东西而存在的。也就是说,可以重构“再一步”并将现在的匿名类命名为本地类(当然仍然在你的方法中)所以你的调用run() 并不难找到。除此之外,我没有真正的重构建议。

关于java - 如何在 Java 中概括方法调用(以避免代码重复),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11355339/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com