【问题标题】:How to write generic Java API without method Overloading如何在没有方法重载的情况下编写通用 Java API
【发布时间】:2018-11-03 15:59:47
【问题描述】:

我有 2 个(或更多)存储相同内容的数据源;我想编写一个接口,其中包含在其中查找项目的方法。示例:

public interface CarFinder {    
    public Car findById(String id);
}

然后我可以写一个这样的类并使用它:

public class CustomCarFinder implements CarFinder {

    public Car findById(String id) {
        ...
        return someCar;
    }
}
...
Car aCar = customCarFinder.findById("1");

CustomCarFinder 知道如何连接到数据源并为我检索Car。 问题是,对于我的第一个数据源,CustomCarFinder 每次我调用“findById”时都可以连接到它;对于第二个数据源,知道如何获取连接的是CarFinder 的客户端,而不是CarFinder。为了向CarFinder 提供连接信息,我写了如下内容:

public interface CarFinder {

    public Car findById(String id, Object... context);

}

public class CustomCarFinder implements CarFinder {

    public Car findById(String id, Object... context) {
        //The Varargs (context) are not used in this version
        ...
        return someCar;
    }
}

public class AnotherCustomCarFinder implements CarFinder {

    public Car findById(String id, Object... context) {
        //Extract the connection here from the Varargs
        CustomConnection connection = (CustomConnection)context[0];
        ...
        //Somehow I find the car via this CustomConnection thing
        return someCar;
    }
}
...
Car aCar = customCarFinder.findById("1");
Car anotherCar = anotherCustomCarFinder.findById("1", aCustomConnection);

您看到我使用了可变参数,以便我可以使用 API 的任一版本或版本。在不需要提供连接的第一种情况下,我仍然可以使用:

Car aCar = customCarFinder.findById("1");

如果我需要提供连接,那么:

Car anotherCar = anotherCustomCarFinder.findById("1", aCustomConnection);

Finder 类是作为 Spring 单例实现的,因此它们是共享的,因此为了避免线程问题,它们是无状态的,所以我不想在使用这些方法之前设置“连接”;这就是为什么我将连接作为可变参数传递。

还有其他方法可以做同样的事情吗? 在使用 Varargs 时,我收到了(来自同事的)反对意见,我应该用不同类型的 Connection 类型重载“findById”方法。 我反对这一点,因为我不希望界面反映我正在连接的数据源的类型。如果可能的话,我希望界面保留:

public Car findById(String id);

我也不喜欢 Varargs,但我不知道如何摆脱它们并仍然完成我想要的。

【问题讨论】:

  • 有什么理由不能将自定义连接作为查找器的属性?
  • Joe,我刚刚更新了提到这一点的问题:“Finder 类作为 Spring 单例实现,因此它们是共享的,因此为了避免线程问题,它们是无状态的,所以我不想设置使用方法之前的“连接”;这就是我将连接作为可变参数传递的原因。”

标签: java design-patterns variadic-functions overloading generic-programming


【解决方案1】:

可变参数在需要时很好用,但在这种情况下,我认为最好有一个用于连接的设置器。

要使此内容可跨线程共享,您可以使用

public class AnotherCustomCarFinder implements CarFinder {
    private Pool<CustomConnection> connectionPool;

    public void setConnectionPool(Pool<CustomConnection> connectionPool) {
        this.connectionPool = connectionPool;
    }

    public Car findById(String id) {
        CustomConnection connection = connectionPool.acquire();
        //Somehow I find the car via this CustomConnection thing
        connectionPool.release(connection);
        return someCar;
    }
}

这样你就可以写了

Car aCar = customCarFinder.findById("1");
Car anotherCar = anotherCustomCarFinder.findById("1");

【讨论】:

  • 抱歉忘了提到 Finder 类是 Spring 单例,它们是共享的,所以我不能真正设置“全局”连接,因为另一个线程可能会在其他方法运行时设置连接依赖于另一个连接。因此,我们在每个方法调用中传递连接以避免线程问题、同步等。因此,Finder 是无状态的。
  • @GustavoT 虽然这听起来不错,但问题是您的调用者需要了解 Finder 的工作原理,以了解您需要提供的内容。如果你有一个全局连接池,你可以传递它而不是每个线程一个单独的。
  • 不幸的是没有游泳池。第二个数据源是一个奇怪的源,当有人调用它提供的 REST API 时,它使上下文可用。只能从 API 调用内部的该上下文中获取连接(它们提供了一个插件方法,您可以在其中将自己插入 API 调用)。因此,可以通过呼叫连接获得连接;它们不能被池化或缓存;一旦 REST 调用结束,连接就失效了。
  • @GustavoT 所以你需要一个Supplier&lt;CustomConnection&gt;,它应该很容易写。
【解决方案2】:

我假设你可以使用 Java 8。

如果您不喜欢 Varargs,您可以将 Supplier 传递给该方法,如下所示:

interface CarFinder {
    Car findById(String id, Supplier<Object> sup);
}

class CustomCarFinder implements CarFinder {
    public Car findById(String id, Supplier<Object> sup) {
        // ignore the sup
        // and the rest
    }
}

class AnotherCustomCarFinder implements CarFinder {
    public Car findById(String id, Supplier<Object> sup) {
        CustomConnection conn = (CustomConnection)sup.get();
        // and the rest
    }
}

现在你可以使用它们了:

customCarFinder.findById("1", () -> null);
anotherCustomCarFinder.findById("1", () -> customConnection);

如果你也不喜欢null(我猜)你可以通过dummyConnection

【讨论】:

    猜你喜欢
    • 2021-10-06
    • 2019-03-06
    • 2018-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多