2

我今天的问题不是解决问题的问题,而是最佳实践理论问题。我正在熟悉 Joda-Time,并在用户指南 ( http://joda-time.sourceforge.net/userguide.html#Interface_usage ) 中阅读了此内容:

使用 Collections 接口(例如 List 或 Map)时,您通常会将变量保存为 List 或 Map 的类型,仅在创建对象时引用具体类。

 List list = new ArrayList();
 Map map = new HashMap();

这让我觉得很奇怪。在我的编程中,我总是为我的变量保留具体的类,除非我正在编写某种 API。有没有人有任何有用的材料来解释为什么优先为您的变量类型保留通用/抽象类而不是具体类的原因?

为了说明,我编写了一个简单的方法,每当我需要一个对象列表作为用逗号分隔的字符串时,我都会使用它:

public static <T> String CSV(Collection<T> list) {
    boolean first = true;
    StringBuilder sb = new StringBuilder();
    for(Object e : list) {
        if(first)
            first = false;
        else
            sb.append(", ");
        sb.append(e.toString());
    }
    return sb.toString();
}

当然,这里你必须使用 Collection,这样你就可以传入任何你想要的东西,Object[]、LinkedList 等。

但是让我们在别处说我正在存储一组字符串,并且我希望它是一个 LinkedHashSet,我会像这样创建变量:

LinkedHashSet<String> set = new LinkedHashSet<String>();

或者

LinkedHashSet<String> set = new LinkedHashSet<>();

(因为 Java 7 的变化)

但根据 Joda-Time 的用户指南,我应该这样做:

Set<String> set = new LinkedHashSet<String>();

?

我只是没有看到优势。有人有有用的输入/阅读材料吗?

谢谢!

4

5 回答 5

2

如果您使用Collection(例如)它并不重要,它是如何Collection实现的 - 您只想使用Collection方法,仅此而已。在这种情况下,您的代码将非常敏捷,您可以轻松更改Collection实现。但是,如果基本界面还不够 - 请使用具体参考。

这是 OOP 基础——如果可能,您应该使用抽象,而不是使用具体类。

类似的问题:

于 2013-05-09T15:59:49.287 回答
0

由于Java(还)没有类型推断,因此您必须在定义变量时显式指定变量,并且每次基于它创建新变量时。如果该类型是一个具体的类,这意味着如果您以后想要更改它,您还必须检查并更改所有这些声明。重构工具可能会使其自动化,但这仍然是一个小麻烦。使用抽象或接口类型允许您更改具体类型而不更改所有这些声明。

总的来说,我不认为这有什么大不了的。

于 2013-05-09T15:57:01.690 回答
0

如果您正在编写一些将被其他人使用或修改的代码,那么使用接口并让他们选择最佳实现会更容易,而不是选择具体类型而强迫他们使用您的类型。

于 2013-05-09T16:03:54.167 回答
0

您通常希望使用最接近定义所需功能的接口。

以下是使用 Queue over LinkedList 的一些原因:

  • 队列告诉读者如何使用一个变量——它更具描述性。
  • 您可以用任何立即实现 Queue 的东西替换对象的实现。
  • 如果您使用 DI,您甚至不必知道队列的实现,并且可以在以后更改它,甚至无需修改代码。
  • 如果只使用 Queue 方法,那么使用 LinkedList 而不是 Queue 没有任何优势。
  • 编译器可以断言您的队列没有被不当使用。

出于多种原因,最后一个非常重要。如果您编写一个返回“队列”的方法,您可以期望它不会被不恰当地处理,并且您会立即向用户准确地描述他们的新对象是如何被操纵的。例如,它不打算作为列表进行迭代。

这也适用于您自己的接口,您可能希望创建一个接口来为一个目的公开功能子集,同时为另一个目的公开不同的子集......例如,您可以将相同的队列返回给两个不同的调用者,一个具有“入队”接口和具有“出队”接口的接口-非常清楚“管道”应该流向哪个方向。

于 2013-05-09T16:05:57.073 回答
0

我对实例化的一般经验法则是使用最适合您需要的基类。

我的意思是在这种情况下,您使用的是 LinkedHashSet,如果您知道您只会使用由 Set 的契约实现的方法,请将其实例化为 Set。这样做的原因是它使您的代码更具前瞻性。例如,如果您需要返回并确保 Set 是线程安全的,则实例化 Concurrent 类而不是非线程安全的 LinkedHashSet。这样,您的下游代码不会改变,您只需要更新实例化。

于 2013-05-09T16:11:44.533 回答