Когда я должен использовать класс с 2 свойствами для предварительно созданной структуры, такой как KeyValuePair?

31

Когда следует помещать данные типа Key / Value в их собственный класс вместо использования предварительно созданной общей структуры, такой как a KeyValuePairили a Tuple?

Например, большинство ComboBox, которые я создаю, содержат DisplayName и Value. Это тот тип данных, который я пытаюсь решить, когда добавить новый класс, а когда просто использовать KeyValuePair.

В настоящее время я работаю над тем, что использует iCalendar, и данные выбранного пользователя в конечном итоге объединяются в key1=value1;key2=value2;тип строки. Я начал с помещения данных в a KeyValuePair<string,string>, но теперь мне интересно, должен ли это быть его собственный класс.

В целом, мне интересно узнать, какие руководящие указания используются при принятии решения об использовании существующей структуры / класса, например, KeyValuePairобъекта с двумя свойствами, и в каких ситуациях вы будете использовать один над другим.

Рейчел
источник
3
Какой язык? В Python у нас нет этой дилеммы, потому что у нас есть tupleтип.
S.Lott
3
@ S.Lott - .NET BCL имеет кортежи начиная с версии 4.0.
Передано
1
.NET 4 также имеет тип кортежа. msdn.microsoft.com/en-us/library/system.tuple.aspx
Дейв Най
1
@Oded В этом случае я что-то строю iCalendarи хочу объекты для BYDAYи BYSETPOS. Они появляются в комбинированных списках, но фактические данные объединяются в повторяющуюся строку правила, которая является key=value;типом строки
Rachel
4
@ Рейчел: Пожалуйста, обновите вопрос, чтобы быть более конкретным. Куча комментариев - не лучший способ прояснить ситуацию.
С.Лотт

Ответы:

26

Я бы обычно использовал объект, а не KeyValuePair или Tuple в большинстве случаев. Во-первых, когда вы приходите через 6 месяцев, чтобы внести изменения, гораздо проще выяснить, каким было ваше намерение раньше, чем задаваться вопросом, что Tuple tи почему оно имеет эти забавные ценности. Во-вторых, по мере роста и изменения вы можете легко настроить поведение ваших простых объектов передачи данных в соответствии с требованиями. Нужно иметь два формата имени? Легко, просто добавьте соответствующие перегрузки ToString (). Нужно ли реализовать какой-то интерфейс? Нет проблем. Наконец, создание простого объекта практически не требует дополнительных затрат, особенно с автоматическими свойствами и завершением кода.

Дополнительный совет: если вы хотите предотвратить загрязнение пространства имен этими объектами, создание закрытых классов внутри классов - отличный способ сохранить все в тайне и предотвратить странные зависимости.

Уайетт Барнетт
источник
1
DTOs = объекты передачи данных ? - Я ненавижу TLA ;-)
Бен
3
@Ben - TFTFY. , ,
Уайетт Барнетт
7

Правило определения нового класса простое: оно обобщено «Бритвой Оккама».

http://c2.com/cgi/wiki?OccamsRazor

Не вводите новые классы без уважительной причины. Или используйте готовые классы как можно больше. Или изобретать как можно меньше. Однако вы можете писать меньше кода.

Вы не можете использовать предварительно созданный класс, если вам необходимо назначить объекту уникальную ответственность, и эта ответственность не определена в предварительно созданном классе. Часто это уникальный метод.

А tupleили KeyValuePairявляется предпочтительным.

До того как.

Вам нужны некоторые функции, которые не являются частью A tupleили KeyValuePair. Затем вы должны определить свой собственный класс.

С. Лотт
источник
3
« Никогда не придумывайте, что вы можете украсть ». Это мой любимый способ выразить это.
SingleNegationElimination
1
Считаете ли вы семантику «функциональностью, которая не является частью tupleили KeyValuePair»? KeyValuePair, по крайней мере, имеет ограниченную форму семантики, но кортежи редко (возможно, в зависимости от контекста) imho. Введение нового класса должно дать вам четкую семантику как само собой разумеющееся.
Мал Росс
+1 Не заправляйте целое в лодке с помощью клейкой ленты, если в нее может поместиться вилка.
Эван Плейс
4
Я думаю, что это неуместное использование бритвы Оккама. Во-вторых, важно знать, какая информация содержится в переменной. -1.
согнуло
4

Если вы пишете свой собственный класс, у вас есть четкое место для размещения документации о двух значениях. Тем более, что две строки не очень четкое определение. Однако вы могли бы написать что-то вроде

 public class MyPair : Tuple<string, string>
Подписать
источник
Мне это нравится, потому что MyPairопределяет значения Tuple и для чего они используются.
IAbstract
2
Но тогда ваши свойства будут называться Item1и Item2! Разве не так просто определить весь класс? public class SideStrings { public string Left { get; set; } public string Right { get; set; } }
конфигуратор
@configurator Я писал ответ на это и заметил, что Tuple только для чтения, так что я согласен, что, как мне кажется, все это поддерживает. Хотя вы можете обернуть кортеж, чтобы назвать значения как хотите.
Знак
2
@Sign: Но какой в ​​этом смысл? Создание собственного класса - это меньше работы, чем обертка кортежа.
конфигуратор
2
@configurator, вы получаете некоторые вещи для сравнения и сравнения, которые работают лучше, чем пользовательский объект, но если требуется установка, то кортеж - определенно неправильный путь.
Подпишите
4

С. Лотт написал

Не вводите новые классы без уважительной причины. Или используйте готовые классы как можно больше. Придумайте как можно меньше.

Вы не можете использовать предварительно созданный класс, если вам нужно назначить некоторую уникальную ответственность для объекта, который не определен в предварительно созданном классе.

Позвольте мне сказать, почему у меня есть серьезная проблема с этим. поскольку

KeyValuePair [строка, строка]

кажется, все в порядке .... Является ли KeyValue [строка, KeyValue [строка, KeyValuePair [строка, строка]]] также хорошо? Я не знаю, что С.Лотт скажет на это, но мой босс считает, что все в порядке.

Смею не согласиться. И вот почему: это снижает читабельность кода, который будет следовать. Функция, которая заполнит такую ​​структуру данных, будет намного более сложной и подверженной ошибкам (ошибкам… исключение) (представьте себе хотя бы некоторую бизнес-логику). Я не слишком спорил с моим боссом, но я скажу это здесь: удобочитаемость превосходит минимальную экономию места (что было его аргументом). Так не будет объект класса, в котором есть несколько полей; но некоторые остаются пустыми, иногда лучше?

PS Я Ultra_Noob, поэтому, пожалуйста, скажите мне, если я не прав.

Чани
источник
2
Я думаю, KeyValuePair<string,string>это нормально, если предположить, что вложенные вещи действительно являются ключами и ценностями. Здесь повторное использование существующего класса является выигрышем, поскольку вы сохраняете написание кода и получаете совместимость. OTOH, используя что-то настолько сложное, насколько KeyValuePair<string,KeyValuePair<string,string>>это часто бывает просто слишком сложно, чтобы быть практичным. Иногда это может быть правильным - когда вам не нужны дополнительные функции, когда смысл очевиден и когда он прекрасно работает с другими классами. YMMV, это просто взвешивание плюсов и минусов.
Маартин
Если вы используете C #, вот производительность Tuple vs KeyValuePair . Что-то, чем вы можете поделиться со своим боссом?
Бен
3

Когда мы говорим пара ключ / значение , я обычно имею в виду хеш-таблицы , или ассоциативные массивы , или просто объект KeyValuePair . Я использую все из них, и нет разницы в том, когда я использую какой.

Я думаю, что самая важная вещь в списке пар ключ / значение состоит в том, что ключи должны быть уникальными (поскольку это ключ в конце концов), и все вышеупомянутые структуры действительно предоставляют мне эту функциональность.

Кроме этого, я хочу осуществлять поиск по ключам или выполнять действия в стиле списка (массива), такие как push и pop.

Итак, мой ответ - НЕТ , и я не создаю объекты явно для пар ключ / значение, так как многие встроенные структуры уже делают это для меня.

Саид Нямати
источник
3

В шестой главе Чистого кода есть хорошее описание того, когда использовать структуру данных по сравнению с классом. Короче говоря, вы используете структуру данных, когда в основном ожидаете добавления новых функций для работы с этими данными. Вы используете класс, когда вы в основном ожидаете добавления новых типов данных для работы с существующими функциями. Ваш ComboBox является примером последнего. У вас есть один набор функций (реализация ComboBox), работающих с множеством разных типов данных (разные виды данных для разных виджетов).

Карл Билефельдт
источник
2

Я бы, возможно, согласился бы с Уилдингом здесь , но тогда я тоже немного новичок в подобных вещах.

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

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

Итак, настоящий вопрос: представляете ли вы данные или используете данные в механизме? (и я бы не советовал смешивать эти понятия вместе).

По моему опыту, нет ничего хуже, чем видеть, как Tuple [string, string] передается в метод и не имеет непосредственного способа узнать, какими должны быть Item1 и Item2. Лучше всего использовать кортежи в методах или как частные члены класса.

Бен
источник