为什么使用元组而不是对象?

我工作的代码库有一个名为Pair的对象,其中A和B是Pair中第一个和第二个值的types。 我觉得这个对象是冒犯的,因为它被用来代替一个明确指定成员的对象。 所以我觉得这个:

List<Pair<Integer, Integer>> productIds = blah(); // snip many lines and method calls void doSomething(Pair<Integer, Integer> id) { Integer productId = id.first(); Integer quantity = id.second(); } 

代替

 class ProductsOrdered { int productId; int quantityOrdered; // accessor methods, etc } List<ProductsOrderded> productsOrdered = blah(); 

在代码库中对的许多其他用途同样是不好闻的。

我使用了元组,他们似乎经常被误解或用于可疑的方式。 有没有一个令人信服的论点来支持或者反对这种使用? 我可以理解,不想创build庞大的类层次结构,但是如果不使用元组,那么是否存在实际的代码库,类层次结构会发生爆炸?

首先,一个元组快速而简单:不是每次你想把两样东西拼在一起写一个类,而是有一个模板为你做。

其次,它们是通用的。 例如,在C ++中,std :: map使用一个std :: key和value对。 因此可以使用ANY对,而不必为两种types的每个置换使用访问器方法创build某种包装类。

最后,它们对于返回多个值很有用。 真的没有理由为一个函数的多个返回值创build一个类,如果它们不相关,就不应该被当作一个对象。

公平地说,你粘贴的代码是一对糟糕的使用。

元组在Python中一直使用,它们被集成到语言中,非常有用(它们允许启动器有多个返回值)。

有时候,你真的只需要配对,创造一个真正的,诚实的上帝,上课是矫枉过正的。 另一方面,当你真正使用一个类的时候使用元组就是一个相反的想法。

代码示例有一些不同的气味:

  • 重新发明轮子

框架中已经有了一个元组, KeyValuePair结构。 Dictionary类使用这个来存储对,但是你可以在任何适合的地方使用它。 (不是说它适合这种情况…)

  • 制作一个方形的轮子

如果你有一个对的列表,最好使用KeyValuePair结构,而不是具有相同目的的类,因为它会导致内存分配的减less。

  • 隐藏的意图

具有属性的类清楚地显示了这些值的含义,而Pair<int,int>类并没有告诉你任何关于值表示的东西(只是它们可能以某种方式相关)。 为了使代码合理地自我解释,像这样的列表,你必须给列表一个非常简短的名字,像productIdAndQuantityPairs

对于它的价值,OP中的代码乱七八糟,不是因为它使用了元组,而是因为元组中的值太弱了。 比较以下内容:

 List<Pair<Integer, Integer>> products_weak = blah1(); List<Pair<Product, Integer>> products_strong = blah2(); 

如果我的开发团队在周围传递ID而不是类实例,我也会感到不安,因为一个整数可以代表任何东西


据说,元组在使用它们的时候非常有用:

  • 存在元组以将特定值组合在一起。 它们肯定比创build过多的包装类更好。
  • 当您需要从函数返回多个值时,可以使用out / ref参数的有用替代方法。

但是,C#中的元组使我的眼睛水。 像OCaml,Python,Haskell,F#等许多语言都有定义元组的特殊简洁的语法。 例如,在F#中, Map模块定义了一个构造函数,如下所示:

 val of_list : ('key * 'a) list -> Map<'key,'a> 

我可以创build一个地图的实例:

 (* val values : (int * string) list *) let values = [1, "US"; 2, "Canada"; 3, "UK"; 4, "Australia"; 5, "Slovenia"] (* val dict : Map<int, string> *) let dict = Map.of_list values 

C#中的等价代码是可笑的:

 var values = new Tuple<int, string>[] { new Tuple<int, string>(1, "US"), new Tuple<int, string>(2, "Canada"), new Tuple<int, string>(3, "UK"), new Tuple<int, string>(4, "Australia"), new Tuple<int, string>(5, "Slovenia") } var dict = new Dictionary<int, string>(values); 

原则上我不相信元组有什么问题,但是C#的语法太麻烦了,以至于无法使用它们。

这是代码重用。 我们制作了最后的5个元组类,而不是编写与另一个类完全相同的结构类,而是使用一个Tuple类,并在需要元组时使用它。

如果类的唯一意义是“存储一对值”,那么我会说使用元组是一个明显的想法。 如果你开始实现多个相同的类,那么你可以重命名这两个成员,我会说这是一种代码味道(尽pipe我讨厌这个词)。

这只是原型质量的代码,可能被砸在一起,从来没有被重构。 没有修复它只是懒惰。

元组的实际用途是为了实际上不关心组件部分是什么的genericsfunction,而只是在元组级别上运行。

Scala具有元组types,从2元组(对)到20+元组。 查看Scala的第一步(第9步):

 val pair = (99, "Luftballons") println(pair._1) println(pair._2) 

如果您需要将某些相对特定目的的值捆绑在一起,则元组很有用。 例如,如果你有一个需要返回两个完全不相关的对象的函数,而不是创build一个新的类来保存这两个对象,你从函数返回一个Pair。

我完全同意其他海报,元组可以被滥用。 如果一个元组有任何对你的应用程序都很重要的语义,你应该使用适当的类。

一个明显的例子是坐标对(或三重)。 标签是不相关的; 使用X和Y(和Z)只是一个惯例。 使他们统一,清楚地表明他们可以用同样的方式对待。

已经提到了很多东西,但我想也应该提一下,有一些编程风格,与OOP不同,这些元组相当有用。

像Haskell这样的函数式编程语言根本就没有类。

如果你正在做一个Schwartzian变换来sorting一个特定的键(这是昂贵的重复计算),或类似的东西,类似乎有点矫枉过正:

 val transformed = data map {x => (x.expensiveOperation, x)} val sortedTransformed = transformed sort {(x, y) => x._1 < y._1} val sorted = sortedTransformed map {case (_, x) => x} 

有一个class DataAndKey或任何似乎有点多余在这里。

你的例子不是一个元组的好例子,但我同意。