Код, который преобразует значение в другое представление, а затем преобразовывает его обратно в исходное состояние, является плохим, но как? [закрыто]

35

Я читал статью о плохих практиках программирования .

Это упомянуто -

«Код Yo-Yo», который преобразует значение в другое представление, а затем преобразует его обратно в исходное положение (например, преобразование десятичного числа в строку и затем обратно в десятичное, или заполнение строки и затем ее обрезку)

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

Кто-нибудь может объяснить больше об этом?

user13107
источник
4
рекомендуемое чтение: Обсудите этот $ {blog}
gnat
8
В большинстве случаев это просто избыточно, и это происходит только потому, что программист не знал лучшего способа получить то, что хотел. Запись в блоге дает типичный пример несколько абзацев позже: "Roundabout code" that accomplishes in many instructions what could be done with far fewer (eg: rounding a number by converting a decimal into a formatted string, then converting the string back into a decimal). if the situation is so that they have to be used?- что это будет за ситуация?
Конрад Моравский
3
@gnat Я не понимаю, как это делает этот вопрос плохим. Если вы хотите, я могу отредактировать его, чтобы сказать: «Код, который преобразует и преобразует обратно значение, плохо?» и это больше не будет соответствовать этому шаблону.
джехлин
5
Недавно я нашел в Java код, который перебирает массив, сериализуя каждый объект в объект JSON, используя конкатенацию строк, а не сериализатор JSON. Затем результат был передан в закрытый метод, который анализировал массив JSON для извлечения группы идентификаторов, а затем передавал идентификаторы куда-то еще. Это было единственное использование этого массива JSON в системе. Это код йо-йо. Не было никаких оснований для трансформации взад и вперед. Мы могли бы просто передать идентификаторы из оригинальных объектов.
Брэндон
3
decimal myValue = decimal.Parse(dataReader["myColumn"].ToString())моя любимая мозоль.
Мэтью

Ответы:

125

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

Принцип, как всегда, в том, что код, который не существует, не может иметь тонких дефектов , в то время как код, который существует, часто имеет. Это может показаться параноидальным, но опыт учит нас, что это уместно. Если вы подходите к программированию с постоянным легким беспокойством «я не настолько умен, чтобы понимать эту сложную систему», вы на правильном пути.

Килиан Фот
источник
5
Хорошо сказано. Мы, программисты, должны быть очень осторожными.
Нил
58
«Код, который не существует, не может иметь тонких дефектов, в то время как код, который существует часто, делает», жаль, что я не смогу +2 за это. Никогда не стоит недооценивать ценность отсутствия необходимости писать код.
Бенджамин Грюнбаум
2
Но выполнение пары простых операций (преобразование в строку и обратно) может быть гораздо менее сложным (проще для понимания и кодирования), чем «правильный» способ манипулирования битами. И держать все данные категории в единой форме часто является хорошей идеей, даже если определенные данные будут неизбежно преобразованы в другие формы.
Даниэль Р Хикс
36
@DanielRHicks, так что давайте преобразовать эту простую дату (10 ноября 2014 г.) в строку, -> 10-11-2014 и обратно к дате -> (11 октября 2014 г.) эй, подождите что?
Питер Б
20
@PieterB Есть большое немецкое бухгалтерское программное обеспечение, которое не работает на компьютерах с не немецким языком, потому что это делает это. Сначала он преобразует дату в строку, используя системную локаль, а затем пытается проанализировать ее с фиксированной локалью и жалуется на недопустимый формат. Он делает то же самое с числами и различными десятичными разделителями, за исключением того, что он не жалуется там, он портит данные и демонстрирует странное поведение. Мне потребовались дни, чтобы понять это.
CodesInChaos
23

Это плохо по трем основным причинам:

  1. Это показывает, что вы не думали о том, каким типом / форматом должна быть переменная, но вместо этого конвертируете ее в то, что вам нужно в данный момент. Это показывает отсутствие дизайнерской мысли.
  2. Это вероятно расточительно. Вы почти наверняка тратите впустую циклы и строки кода, выполняя преобразования, которые не должны быть там. Это сделает ваш код медленнее и более раздутым, чем нужно.
  3. Преобразования типов подвержены тонким ошибкам. Расставляя эти преобразования в своем коде, вы увеличиваете вероятность ошибки.

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

Джек Эйдли
источник
6

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

В качестве примера, где преобразование является хорошим: у
каждого есть четыре floatзначения произвольных знаков, величины которых могут различаться с коэффициентом до 1000, и необходимо вычислить сумму с точностью до 0,625 единиц в последнем месте. Преобразование всех четырех значений в double, вычисление суммы и обратное преобразование результата floatбудет гораздо более эффективным, чем любой другой подход, использующий floatодин.
Значения с плавающей точкой в ​​лучшем случае с точностью до 0,5 единиц в последнем месте (ULP). Этот пример потребует, чтобы ошибка округления в худшем случае была не более чем на 25% выше оптимальной ошибки в худшем случае. Использование double даст значение, которое будет точным в пределах 0.5001 ULP. Хотя требование ULP 0,625 может показаться надуманным, такие требования часто важны в алгоритмах последовательного приближения. Чем точнее указана граница ошибки, тем ниже требование к итерациям в худшем случае.

В качестве примера, где преобразование плохое:
один имеет число с плавающей запятой и желает вывести строку, которая будет уникально представлять его значение. Один из подходов - преобразовать число в строку с определенным количеством цифр, попытаться преобразовать ее обратно и посмотреть, соответствует ли результат.

Но это на самом деле плохой подход. Если десятичная строка представляет значение, которое почти точно находится на полпути между двумя значениями с плавающей запятой, для метода строки-с плавающей запятой довольно дорого гарантировать, что он всегда будет давать более близкое floatзначение, и многие такие методы преобразования не не поддерживать такую ​​гарантию (среди прочего, в некоторых случаях для этого потребуется прочитать все цифры числа, даже если длина его составляет миллиарды цифр).

Для метода намного дешевле гарантировать, что он всегда будет возвращать значение, которое находится в пределах 0,5625 единиц на последнем месте (ULP) от представленного значения. Надежная «обратимая» процедура форматирования десятичных чисел в строку должна вычислять, насколько далеко выходное значение от правильного значения, и продолжать выводить цифры, пока результат не будет в пределах 0,375 (ULP), если не 0,25 (ULP). В противном случае он может вывести строку, которую некоторые методы преобразования будут обрабатывать правильно, а другие методы преобразования - нет.

Иногда лучше вывести цифру, которая может не быть «необходимой», чем вывести значение, которое может быть неправильно истолковано. Ключевым моментом является то, что решение о том, сколько цифр должно быть выведено, должно приниматься на основе численных расчетов, связанных с процессом вывода, а не в результате попытки одного конкретного метода преобразовать строку обратно в число.

Supercat
источник
1
Ваш пример не возвращает исходное значение, о котором спрашивает OP. Он просто возвращает значение одного и того же типа, рассчитанное по нескольким входам.
CJ Деннис
2

Разные причины

  1. Это бессмысленно и добавляет сложности - как в объеме кода для написания и поддержки, так и в количестве необходимого процессорного времени

    1. Он может потерять точность или, что еще хуже, полностью испортить ценность

    2. Это тратит впустую память (потенциально, в зависимости от языка), поскольку вы в конечном итоге сохраняете больше представлений нужного вам числа

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

Джон Стори
источник
это, кажется, не добавляет ничего существенного к пунктам, изложенным и объясненным в предыдущих ответах
комнат
2
Оправдывает ли это голосование против? Я считаю, что мой пост потенциально более кратким
Джон Стори
предыдущие ответы на самом деле выглядят более краткими для меня, оба из них
комнат
0

Зачем? Потому что даже лучшие из нас могут ошибаться.

Посмотрите, что произошло, когда Microsoft попыталась внедрить формат «туда-обратно» специально для того, чтобы убедиться, что преобразования с плавающей запятой <-> были безопасными: https://stackoverflow.com/q/24299692/541686

Mehrdad
источник
0

Когда я был в школе (и после школы в области электротехники), нас учили делить после умножения. Деление часто многозначно и округляется. Умножение после деления умножает ошибку деления.

Преобразования типов одинаковы, вы рискуете потерять данные. CInt (1.3) = 1.

На моем языке Basic мы выполняем только преобразования типов (программа VB6 тратит 90% своего времени на преобразование ANSI / Unicode для всех вызовов API, выполняемых средой выполнения).

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

 Print 5

Строка «5» печатается из числового литерала.

form1.caption = "My Form"

Строковый литерал Юникода преобразуется в строку ANSI и отправляется в SetWindowsTextA пакетом форм.

Даже это работает в основном

a = "5"
b = 3

c = a + b (= 8)

Сейчас я программист по вариантам - я даже не думаю о типе. Я просто полагаюсь на автоконверсии.

Во всяком случае, мои 3 любимые мозоли

Назначение строковых литералов переменным для их использования (тратит память и медленно)

Бессмысленные функции, когда код может быть встроенным (и компилятор, вероятно, отменит вашу функцию и встроит ее в любом случае)

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

и четвертый для коротких программ

Бессмысленно диммируя ваши 3 переменные в 5-строчной программе.

triggeradeadcat
источник