Подтвердить что ты не робот

Зачем использовать кортежи вместо объектов?

В базе кода, где я работаю, есть объект, называемый Pair, где A и B - типы первого и второго значений в Pair. Я считаю, что этот объект оскорбителен, потому что он используется вместо объекта с четко обозначенными членами. Поэтому я нахожу это:

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();

Многие другие виды использования пары в кодовой базе также плохо пахнут.

I Googled кортежи, и они, кажется, часто неправильно понимаются или используются сомнительными способами. Есть ли убедительные аргументы в пользу или против их использования? Я могу оценить, не желая создавать огромные иерархии классов, но существуют ли реалистичные кодовые базы, где иерархия классов взрывается, если кортежи не используются?

4b9b3361

Ответ 1

Прежде всего, кортеж выполняется быстро и просто: вместо того, чтобы писать класс для каждого раза, когда вы хотите соединить 2 вещи, есть шаблон, который сделает это за вас.

Во-вторых, они носят общий характер. Например, в С++ std:: map использует std:: пару ключей и значений. Таким образом, можно использовать любую пару, вместо того, чтобы создавать какой-то класс-оболочку с помощью методов доступа для каждой перестановки двух типов.

Наконец, они полезны для возврата нескольких значений. Там действительно нет причин, чтобы сделать класс специально для функции множественными возвращаемыми значениями, и они не должны рассматриваться как один объект, если они не связаны.

Чтобы быть справедливым, код, который вы вставили, плохо использует пару.

Ответ 2

Кортежи используются все время в Python, где они интегрированы в язык и очень полезны (они допускают множественные возвращаемые значения для стартеров).

Иногда вам просто нужно соединить вещи и создать настоящий, честный бог, класс перебор. С другой стороны, использование кортежей, когда вы действительно должны использовать класс, так же плохо, как и обратная.

Ответ 3

Пример кода имеет несколько разных запахов:

  • Переосмысливание колес

В структуре уже есть кортеж; структура KeyValuePair. Это используется классом Dictionary для хранения пар, но вы можете использовать его везде, где он подходит. (Не говоря, что он подходит в этом случае...)

  • Создание квадратного колеса

Если у вас есть список пар, лучше использовать структуру KeyValuePair, чем класс с той же целью, поскольку это приводит к меньшему распределению памяти.

  • Скрытие намерения

Класс со свойствами ясно показывает, что означают значения, а класс Pair<int,int> не говорит вам ничего о том, что представляют значения (только они, вероятно, связаны каким-то образом). Чтобы сделать код разумно понятным с таким списком, вам нужно было бы дать списку очень desciptive имя, например productIdAndQuantityPairs...

Ответ 4

Для чего стоит, код в OP беспорядок не потому, что он использует кортежи, а потому, что значения в кортеже слишком слабо типизированы. Сравните следующее:

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

Я тоже расстроился бы, если бы моя команда разработчиков проходила вокруг идентификаторов, а не экземпляров класса, потому что целое число может представлять anything.


С учетом сказанного кортежи чрезвычайно полезны, если вы используете их правильно:

  • Корреляции существуют, чтобы группировать специальные значения вместе. Они, безусловно, лучше, чем создание чрезмерного количества классов-оболочек.
  • Полезная альтернатива параметрам out/ref, когда вам нужно вернуть более одного значения из функции.

Однако кортежи в С# делают мои глаза водой. Многие языки, такие как OCaml, Python, Haskell, F # и т.д., Имеют специальный, сжатый синтаксис для определения кортежей. Например, в F # модуль определяет конструктор следующим образом:

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

Я могу создать экземпляр карты, используя:

(* 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

Одинаковый код в С# смехотворен:

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);

Я не верю, что в кортежах нет ничего плохого, но синтаксис С# слишком громоздкий, чтобы максимально использовать их.

Ответ 5

Повторное использование кода. Вместо того, чтобы писать еще один класс с точно такой же структурой, как и последние 5 классов, похожих на тупочки, которые мы создали, вы делаете... класс Tuple и используете его всякий раз, когда вам нужен кортеж.

Если единственным значением класса является "сохранение пары значений", я бы сказал, что использование кортежей - это очевидная идея. Я бы сказал, что это был запах кода (насколько я ненавижу этот термин), если вы начали внедрять несколько одинаковых классов, чтобы вы могли переименовать два члена.

Ответ 6

Это просто код прототипа, который, вероятно, был разбит вместе и никогда не был реорганизован. Не исправлять это просто лень.

Реальное использование кортежей - это универсальная функциональность, которая действительно не заботится о том, что представляют собой составные части, но просто работает на уровне кортежа.

Ответ 7

Scala имеет tuple-значные типы, полностью из двух кортежей (пар) до кортежей с 20 + элементами. См. Первые шаги до Scala (шаг 9):

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

Кортежи полезны, если вам нужно объединить значения для некоторых относительно специальных целей. Например, если у вас есть функция, которая должна возвращать два довольно несвязанных объекта, вместо создания нового класса для хранения двух объектов, вы возвращаете пара из функции.

Я полностью согласен с другими плакатами в том, что кортежи могут быть неправильно использованы. Если кортеж имеет какую-либо семантику, важную для вашего приложения, вместо этого вы должны использовать правильный класс.

Ответ 8

Очевидным примером является координатная пара (или тройка). Этикетки не имеют значения; использование X и Y (и Z) является просто конвенцией. Формирование их однородности дает понять, что с ними можно обращаться одинаково.

Ответ 9

Многие вещи уже упоминались, но я думаю, следует также упомянуть, что существуют некоторые стили программирования, которые отличаются от ООП и для этих кортежей весьма полезны.

Функциональные языки программирования, такие как Haskell, например, вообще не имеют классов.

Ответ 10

Если вы делаете преобразование Шварца), чтобы сортировать по определенному ключу (который дорого вычисляется многократно) или что-то в этом роде, класс кажется немного лишним:

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 или что-то вроде немного лишнего здесь.

Ваш пример не был хорошим примером кортежа, но я согласен.