Односимвольные константы лучше литералов?


127

Недавно я столкнулся с классом, который предоставляет почти каждый отдельный символ в качестве константы; все от COMMAдо BRACKET_OPEN. Интересно, было ли это необходимо; Я прочитал «статью», в которой говорится, что может быть полезно перенести односимвольные литералы в константы. Итак, я скептически.

Основная привлекательность использования констант состоит в том, что они минимизируют обслуживание, когда необходимо изменение. Но когда мы собираемся начать использовать символ, отличный от ',' для представления запятой?

Единственная причина, по которой я вижу использование констант вместо литералов, - сделать код более читабельным. Но является ли city + CharacterClass.COMMA + state(например) действительно более читабельным, чем city + ',' + state?

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



33
Хм ... это может быть полезно для разных регионов, может быть? Например, некоторые языки используют guillements (угловые кавычки «и ») в качестве кавычек вместо стандартных английских "(или более привлекательных и ). Кроме того, это просто звучит как набор магических персонажей. Предполагая, что два экземпляра CharacterClassвызывают englishCharsи frenchChars, возможно, это englishChars.LEFT_QUOTEможет быть , а frenchChars.LEFT_QUOTEможет быть «.
Джастин Тайм

4
Существует много разных вариантов запятых: en.wikipedia.org/wiki/Comma#Comma_variants - возможно, это не такая глупая идея, особенно если ваш исходный код может быть закодирован как utf-8.
Аарон Холл

21
В вашем случае это похоже на вызов переменной «число». Ваша константа должна была называться DELIMITER. Или это должно быть CITY_STATE = "{0}, {1}"
the_lotus

13
Та статья, которую вы связали, очень ужасна. Константы никогда не должны быть брошены в такое ведро. Поместите их в классы, где они имеют контекст: по сути, класс с константой обеспечивает контекст, в котором используется константа. Например, в Java File.separator. Класс сообщает вам тип разделителя. Именование класса Constsили Constantsотсутствие контекста затрудняет правильное использование констант.

Ответы:


183

Тавтология :

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

Здравый смысл говорит вам, что const char UPPER_CASE_A = 'A';или const char A = 'A'не добавляет ничего, кроме обслуживания и сложности вашей системы. const char STATUS_CODE.ARRIVED = 'A'это другой случай.

Предполагается, что константы представляют вещи, которые являются неизменными во время выполнения, но, возможно, потребуется изменить их в будущем во время компиляции. Когда будет const char A =правильно равен чему-либо, кроме A?

Если вы видите public static final char COLON = ':'в коде Java, найдите того, кто это написал, и сломайте его клавиатуру. Если представление COLONнавсегда изменится у :вас будет кошмар обслуживания.

Obfuscation:

Что происходит, когда кто-то меняет его, COLON = '-'потому что там, где они его используют, нужен -везде? Собираетесь ли вы писать модульные тесты, которые в основном говорят assertThat(':' == COLON)для каждой constссылки, чтобы убедиться, что они не меняются? Только чтобы кто-то исправил тест, когда он их поменял?

Если кто-то действительно утверждает, что public static final String EMPTY_STRING = "";это полезно и полезно, вы просто квалифицируете его знания и спокойно игнорируете их во всем остальном.

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

Сплоченность:

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

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

Связь:

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

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

Если вы используете более подходящее имя, как у DELIMITER = ','вас, у вас все равно будет та же проблема, потому что имя является общим и не несет семантики. Переназначение значения не более помогает анализу воздействия, чем поиск и замена литерала ','. Потому что какой-то код использует его и нуждается в ,каком-то другом коде, но нуждается ;сейчас? Все равно придется смотреть на каждое использование вручную и менять их.

В дикой природе:

Я недавно провела рефакторинг 1,000,000+ LOCприложения, которому было 18 лет. У него были такие вещи, как public static final COMMA = SPACE + "," + SPACE;. Это ни в коем случае не лучше, чем просто указывать, " , "где это необходимо.

Если вы хотите аргументировать читабельность, вам нужно научиться настраивать свою среду IDE для отображения whitespaceсимволов, где вы можете их видеть или что-то еще, - это просто ленивая причина для введения энтропии в систему.

Он также ,определен несколько раз с несколькими ошибками в написании слова COMMAв нескольких пакетах и ​​классах. Со ссылками на все варианты, смешанные вместе в коде. Это был не что иное, как кошмар, чтобы попытаться что-то исправить, не сломав что-то совершенно не связанное.

То же самое с алфавитом, было несколько UPPER_CASE_A, A, UPPER_A, A_UPPERчто большую часть времени были равны , A но в некоторых случаях не было . Почти для каждого персонажа, но не для всех персонажей.

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

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

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


112
Может быть, вы должны добавить контрпример: const char DELIMITER = ':'было бы на самом деле полезно.
Берги

115
Я хотел бы привести несколько аргументов, что EMPTY_STRINGвыгодно. (1) Я могу гораздо легче найти все варианты использования EMPTY_STRINGв файле, чем найти все варианты использования "". (2) когда я вижу, EMPTY_STRINGя точно знаю, что разработчик предполагал, что эта строка будет пустой, и что это не будет неправильным редактированием или заполнителем для строки, которая будет предоставлена ​​позже. Теперь вы утверждаете, что, выдвигая этот аргумент, вы можете квалифицировать мои знания и безопасно игнорировать меня навсегда. Итак, как вы оцениваете мои знания? И планируете ли вы навсегда игнорировать мой совет? У меня нет проблем в любом случае.
Эрик Липперт

39
@immibis: Мы можем перестать думать об этих вещах как о полезных в контексте управления изменениями. Они постоянные. Они не меняются. Думайте о них как о полезных в контексте людей, ищущих и понимающих семантику кода . Знание того, что что-то является разделителем пары ключ-значение, гораздо полезнее, чем знание, что это двоеточие; это факт о семантической области проблемы программы, а не о ее синтаксисе .
Эрик Липперт

15
@EricLippert: Я любопытное видеть точку других здесь , кто указывают на то , что единственная гарантия , что constобеспечивает то , что он не будет меняться во время выполнения (после компиляции), хотя я согласен с вами , что смысловое значение из constвне гораздо важнее, чем его использование в качестве инструмента управления изменениями. Тем не менее, я, конечно, могу представить себе, const EARLIEST_OS_SUPPORTEDчто оно не только семантически непротиворечиво, но и со временем будет меняться по мере развития программы и удаления старой кучи.
Роберт Харви

16
@DanielJour: Итак, это третий аргумент для EMPTY_STRING; что хорошо спроектированная IDE будет отображать инструменты, которые позволяют мне трактовать эту сущность символически, а не синтаксически. Обобщите это до четвертого аргумента: библиотека инструментов анализа кода, которая находится ниже IDE, может позволить расширенный программный анализ правильности кода на символическом уровне . Разработчик, который хочет воспользоваться преимуществами более продвинутых инструментов, чем те, что были написаны буквально 40 лет назад, должен лишь внести небольшие изменения в свои привычки, чтобы пожинать плоды передовых инструментов.
Эрик Липперт

145

Основная привлекательность использования констант состоит в том, что они минимизируют обслуживание, когда необходимо изменение.

ТОЧНО НЕТ. Это вовсе не причина для использования констант, потому что константы не меняются по определению . Если константа когда-либо изменяется, то она не была константой, не так ли?

Привлекательность использования констант не имеет ничего общего с управлением изменениями и имеет отношение к тому, чтобы сделать программы доступными для написания, понимания и поддержки людьми . Если я хочу знать всюду в моей программе, где двоеточие используется в качестве разделителя URL, то я могу очень легко это знать, если у меня есть дисциплина для определения константы URLSeparator, и я вообще не могу знать это легко, если мне нужно выполнить grep для :и получить каждое место в коде, где :используется для обозначения базового класса, или ?:оператора, или чего-либо еще.

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

Хитрость здесь не в том, чтобы избежать констант, а в том, чтобы назвать их с их семантическими свойствами, а не с их синтаксическими свойствами . Для чего используется константа? Не называйте это, Commaесли бизнес-сфера вашей программы не является типографикой, анализом английского языка или чем-то подобным. Назовите это ListSeparatorили что-то подобное, чтобы прояснить семантику вещи.


42
Хотя я согласен с духом того, что вы здесь говорите, ваши вторые / третьи предложения не совсем верны. Константа может меняться между версиями файла. Фактически, большинство программ, которые я пишу, имеют константу с именем что-то вроде MY_VER, которое содержит номер текущей версии программы, который затем может использоваться во всей оставшейся части программы, а не в виде магической строки, такой как «5.03.427.0038». Как вы говорите, дополнительное преимущество заключается в предоставлении семантической информации.
Монти Хардер

50
Чтобы быть справедливым, смысл константы в том, что она не изменяется во время выполнения после инициализации, а не в том, что она не меняется между компиляциями. С точки зрения компилятора, дело в том, что компилятор может делать предположения, что программа не может его изменить; разрешено ли программисту изменять его при перекомпиляции, это не меняет его постоянство. Также могут быть случаи, когда программное обеспечение принимает аппаратное значение только для чтения, возможно, путем разыменования const volatile T*указателя на предварительно определенный адрес; в то время как программа не может изменить его, аппаратная часть может.
Джастин Тайм

6
@MontyHarder: Хороший вопрос. Мое мнение основано на том факте, что я обычно использую языки, которые различают константы - которые должны быть неизменными - и переменные, которые могут быть назначены один раз - которые могут изменяться от версии к версии, запускаться для запуска или что-то еще. Константа и переменная это разные вещи; один остается неизменным, а другой меняется со временем.
Эрик Липперт

7
@ SteveCox: я согласен; способ, которым C / C ++ характеризует «const», странный и ограниченный. Свойство констант, которое я хочу, заключается в том, что их значения не меняются, и я не могу изменять их в одних функциях, но не в других.
Эрик Липперт

15
«Это вовсе не причина для использования констант, потому что константы не меняются по определению. Если константа когда-либо изменяется, то она не была константой, не так ли?» Изменение констант во время компиляции (очевидно, не во время выполнения) совершенно нормально. Вот почему вы сделали их четко обозначенной «вещью». Конечно, константы OP являются мусорными, но думать о чем-то вроде const VERSION='3.1.2'или const KEYSIZE=1024или как-то еще.
AnoE

61

Нет, это глупо.

Что не обязательно глупо, так это вытягивать подобные вещи в именованные ярлыки по причинам локализации. Например, разделитель тысяч - это запятая в Америке (1 000 000), но не запятая в других регионах. Вставка этого в именованную метку (с соответствующим именем без запятой) позволяет программисту игнорировать / абстрагировать эти детали.

Но делать константу, потому что "волшебные струны плохие" - это просто культивирование груза.


8
Локализация обычно сложнее, чем просто строковые константы. Например, в некоторых языках требуется разделитель списка между всеми элементами списка, в то время как другие исключают разделитель перед последним элементом. Поэтому, как правило , нужно не локализованы постоянные, но локализованные правила .
Влад

19
На самом деле разделитель тысяч не обязательно является разделителем тысяч в других регионах (Китай / Япония). Это даже не установлено после постоянного числа цифр (Индия). Да, и могут быть разные разделители в зависимости от того, является ли это разделителем 1000 или 1000000 (Мексика). Но это меньше проблем, чем не использовать цифры ASCII 0-9 в некоторых локалях (фарси). ux.stackexchange.com/questions/23667/…
Питер

1
@Vlad Локализация намного сложнее, чем это, однако, разделитель тысяч - это хорошо известный пример, который люди узнают.

Это зависит от стратегии локализации ... Вы меняете все константы в своей программе, чтобы перевести это? Или лучше читать значения из файла (или другого хранилища данных), делая их эффективными переменными времени выполнения?
PAULO Эберманн

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

29

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

static const wchar_t HYPHEN = '-';
static const wchar_t MINUS = '-';
static const wchar_t EM_DASH = '-';

Позже вы можете изменить свой код для устранения неоднозначности, переопределив их следующим образом:

static const wchar_t HYPHEN = '-';
static const wchar_t MINUS = '\u2122';
static const wchar_t EM_DASH = '\u2014';

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

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


Для меня это единственная веская причина одна может создать символьные константы
FP

2
Использование -в качестве символа тире весьма обманчиво ... в большинстве шрифтов это слишком мало. (Это даже короче, чем ан тире.)
Paŭlo Ebermann

ОК, не лучший пример. Я начал с stringс а с wchar_tс и использовал стандартную рукопись условности "--"для тира. Но в первоначальном примере использовались отдельные символы, поэтому я переключился, чтобы остаться верным этому вопросу. Есть люди, которые набирают -тире, особенно при работе с шрифтом с фиксированным шагом.
Адриан Маккарти

1
@ PaŭloEbermann Нет, традиционно тире - это ширина символа «m» гарнитуры, а тире - это ширина символа «n».
Dizzley

@ Диззли да, и дефис-ширина <n-ширина <м-ширина.
Пауло Эберманн

22

Константа должна добавить смысл.

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

Если вы используете запятую для какой-либо цели и хотите использовать именованную константу, присвойте ей имя в соответствии с ее назначением. Пример:

  • city + CharacterClass.COMMA + state = плохо
  • city + CITY_STATE_DELIMITER + state = хорошо

Используйте функции для форматирования

Лично я предпочитаю FormatCityState(city, state)и не беспокоюсь о том, как тело этой функции выглядит, пока оно короткое и проходит тестовые случаи.


1
Ах, но запятая не всегда одна и та же запятая. Я мог бы определить COMMA = '\ u0559' или '\ u060C' и т. Д. (См. Unicode) или даже позже превратить его в переменную и прочитать его из файла конфигурации. Таким образом, оно будет иметь то же значение , но просто другое значение. Как насчет этого.
Мистер Листер

2
@MrLister: ЯГНИ. Если у вас есть такая необходимость: отлично! У вас есть прекрасное решение. Но если вы этого не сделаете - не загромождайте свой код, потому что, возможно, вы когда-нибудь могли бы. Кроме того, по моему опыту, если вы попытаетесь ввести абстракции без функции в вашей кодовой базе, люди не очень хороши в том, чтобы быть последовательными. Таким образом, даже если вы определили COMMA с намерением использовать какую-то другую кодовую точку в программе достаточного размера и возраста, такой, чтобы выбор имел значение вообще, вы, вероятно, обнаружите, что константа не использовалась везде, где она должна иметь был (и наоборот, возможно, тоже использовался не по назначению).
Имон Нербонн

17

Идея о том, что постоянная COMMA лучше ','или ","довольно легко разоблачить. Конечно, есть случаи, когда это имеет смысл, например, final String QUOTE = "\"";экономия на читабельности без всяких косых черт, но запрет на символы управления языком, такие как, \ 'и "я не нашел их очень полезными.

Использование final String COMMA = ","не только плохая форма, это опасно! Когда кто-то хочет изменить разделитель с ","на, ";"он может перейти к файлу констант, COMMA = ";"потому что он быстрее делает это, и он просто работает. За исключением того, что вы знаете, все другие вещи, которые теперь используют COMMA, также являются точками с запятой, включая вещи, отправляемые внешним потребителям. Таким образом, он проходит все ваши тесты (потому что весь код маршалинга и демаршаллинга также использовал COMMA), но внешние тесты не пройдут.

Что полезно, так это дать им полезные имена. И да, иногда несколько констант будут иметь одинаковое содержимое, но разные имена. Например final String LIST_SEPARATOR = ",".

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


Как насчет условного определения DISP_APOSTROPHE в качестве символа одинарной кавычки ASCII 0x27 или Unicode (что является более типографски приемлемым представлением апострофа) в зависимости от целевой платформы?
суперкат

3
на самом деле QUOTEпример доказывает, что это тоже плохая идея, поскольку вы присваиваете ее тому, что обычно / обычно называется как DOUBLE QUOTEи QUOTEподразумевает, SINGLE_QUOTEчто более правильно называется APOSTROPHE.

3
@JarrodRoberson Я не чувствую, что цитата подразумевает единственную цитату, лично, но это еще одна веская причина, чтобы устранить двусмысленность, где вы можете!
CorsiKa

2
Мне не нравится QUOTEпример по "Hello, my name is " + QUOTE + "My Name" + QUOTEдругой причине - он делает чтение строк, созданных с его помощью, еще сложнее, это тривиальный пример, и все же он выглядит плохо. О, конечно, вместо конкатенации вы можете использовать токены-заменители, но "Hello, my name is %sMy Name%s".format(QUOTE, QUOTE)может быть и хуже. Но давайте попробуем проиндексировать токены "Hello, my name is {0}My Name{0}".format(QUOTE), не намного лучше. Любая нетривиальная строка, созданная с кавычками, будет еще хуже.
ВЛАЗ

2
@corsiKa - я буду жить с актуальными цитатами. Если я пропущу одну из них, IDE, которую я использую, сразу же начнет жаловаться. Скорее всего, код не будет компилироваться. Это довольно легко обнаружить. Как легко совершить ошибку, когда "My name is" + QUOTE + "My Name" + QUOTEя делал эту ошибку три раза, написав приведенный выше комментарий. Вы можете это заметить? Если это займет у вас немного, то недостающее пространство после того, как это . Вы форматируете строку? В этом случае строка с несколькими токенами для замены будет работать еще хуже. Как мне использовать его, чтобы он был более читабельным?
ВЛАЗ

3

Я проделал некоторую работу по написанию лексеров и парсеров и использовал целочисленные константы для представления терминалов. Для простоты односимвольные терминалы имели код ASCII в качестве числового значения, но этот код мог быть совершенно другим. Таким образом, я бы имел T_COMMA, которому был назначен ASCII-код для ',' в качестве его постоянного значения. Тем не менее, были также константы для нетерминалов, которым были назначены целые числа выше набора ASCII. Посмотрев на генераторы парсеров, такие как yacc или bison, или парсеры, написанные с использованием этих инструментов, у меня сложилось впечатление, что в основном все так и делают.

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

Я могу вспомнить еще несколько единичных случаев, когда имеет смысл использовать константы вместо соответствующих литералов. Например, вы можете определить NEWLINE как литерал '\ n' в окне Unix, но '\ r \ n' или '\ n \ r', если вы используете Windows или Mac Box. То же самое касается парсинга файлов, которые представляют табличные данные; Вы можете определить константы FIELDSEPARATOR и RECORDSEPARATOR. В этих случаях вы фактически определяете константу для представления символа, который выполняет определенную функцию. Тем не менее, если бы вы были начинающим программистом, возможно, вы бы назвали вашу константу-разделитель полей COMMA, не понимая, что вы должны были бы назвать ее FIELDSEPARATOR, и к тому времени, как вы поняли, код будет в производстве, и вы будете на следующем проект,

Наконец, описанная вами практика может иметь смысл в нескольких случаях, когда вы пишете код для обработки данных, закодированных в определенной кодировке символов, скажем, iso-8859-1, но ожидаете, что кодировка изменится позже. Конечно, в таком случае было бы гораздо разумнее использовать библиотеки локализации или кодирования и декодирования, но если по какой-то причине вы не можете использовать такую ​​библиотеку для решения проблем кодирования, используя только константы, придется переопределять в одном файле вместо жестко закодированных литералов, замусоренных по всему исходному коду.

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


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

3

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

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

Я полностью согласен с тем, что DELIMITER=':'сама по себе плохая абстракция, и только лучше, чем COLON=':'(так как последняя полностью обнищала).

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

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

Основная привлекательность использования констант состоит в том, что они минимизируют обслуживание, когда необходимо изменение.

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


2

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

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


2

Заметьте, что вы пытаетесь составить список.

Итак, рефакторинг это как: String makeList(String[] items)

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


0

Если это был класс, написанный как часть приложения вашим коллегой-разработчиком, это почти наверняка плохая идея. Как уже отмечали другие, имеет смысл определять константы, такие как, SEPARATOR = ','где вы можете изменить значение, и константа все еще имеет смысл, но намного меньше, чем константы, имя которых описывает только их значение.

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

  • Математические или физические константы, например PI = 3.14159. Здесь роль константы состоит в том, чтобы действовать как мнемоника, поскольку символическое имя PIнамного короче и более читаемо, чем значение, которое оно представляет.
  • Исчерпывающие списки символов в парсере или клавиши на клавиатуре. Может даже иметь смысл иметь список констант с большинством или всеми символами Юникода, и это может быть в вашем случае. Некоторые символы, такие как Aочевидные и четко узнаваемые. Но вы можете легко сказать , Аи Aдруг от друга? Первый кириллическая буква А , а последняя латинская буква А . Это разные буквы, представленные разными кодовыми точками Unicode, хотя графически они практически идентичны. Я бы предпочел иметь константы CYRILLIC_CAPITAL_AиLATIN_CAPITAL_Aв моем коде, чем два почти одинаково выглядящих символа. Конечно, это бессмысленно, если вы знаете, что будете работать только с символами ASCII, которые не содержат кириллицу. Аналогично: я использую латинский алфавит изо дня в день, поэтому, если бы я писал программу, в которой требовался китайский символ, я бы предпочел использовать константу, а не вставлять символ, который я не понимаю. Для тех, кто ежедневно использует китайские иероглифы, китайский иероглиф может быть очевиден, но латиницу легче представить в виде именованной константы. Итак, как вы видите, это зависит от контекста. Тем не менее, библиотека может содержать символические константы для всех символов, поскольку авторы не могут заранее знать, как будет использоваться библиотека и каким символам могут потребоваться константы для улучшения читабельности в конкретном приложении.

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


-1

Может быть.

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

city + '.' + state

в то время как это довольно трудная ошибка с

city + Const.PERIOD + state

В зависимости от вашей среды интернационализации и глобализации, разница между апострофом ASCII и открытым и закрытым апострофом Windows-1252 (или двойной кавычкой ASCII и открытой и закрытой двойной кавычкой Windows-1252) может быть существенной и, как известно, трудно визуализировать в коде.

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

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


2
что происходит, когда какой-то придурок меняется Const.PERIODна равный ~? Нет оправдания тавтологии именованных символов, она просто добавляет обслуживание и сложность, которые не нужны в современных средах программирования. Собираетесь ли вы написать набор модульных тестов, которые в основном говорят assert(Const.PERIOD == '.')?

3
@JarrodRoberson - Это было бы плохо, конечно. Но у вас было бы столько же проблем, если бы кто-то добавил константу Unicode, которая выглядит почти как запятая, а не как запятая. Как я уже сказал, это не та вещь, которую я бы сделал в новом проекте разработки. Но если у вас есть унаследованная база кода с набором пятнистых тестов, в котором вы несколько раз сталкивались с проблемами апострофов и запятых / апострофов / мерзостей Microsoft, создание разумных констант и указание людям использовать их может быть разумным способом код лучше не тратя год на написание тестов.
Джастин Кейв

3
Ваш унаследованный пример плохой, я только что закончил рефакторинг базы кодов LOC с 1 000 000+, которой 18 лет. У него был каждый печатный символ, определенный несколько раз, даже с разными конфликтующими именами. И много раз названные вещи COMMAбыли фактически установлены = SPACE + "," + SPACE. Да, у какого-то идиота была SPACEпостоянная. Я реорганизовал их ВСЕ, и код стал на порядок лучше читаемым, а сотрудники колледжей были гораздо в состоянии отследить вещи и исправить их, не имея 6 уровней косвенности, чтобы выяснить, что-то на самом деле было установлено.

-1

Односимвольные константы лучше литералов?

Здесь много смущений. Дай мне посмотреть, смогу ли я их дразнить.

Константы обеспечивают:

  • семантика
  • изменить, в процессе разработки
  • косвенность

Переход к одному имени персонажа влияет только на семантику. Имя должно быть полезным в качестве комментария и ясно в контексте. Это должно выражать значение, а не значение. Если это может сделать все это с одним символом, хорошо. Если нет, пожалуйста, не надо.

Буква и константа могут меняться в процессе разработки. Это то, что поднимает проблему магического числа. Строки также могут быть магическими числами.

Если семантическое значение существует, и поскольку оба являются постоянными, то вопрос о том, имеет ли константа большее значение, чем литерал, сводится к косвенному.

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

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

Отклонение может быть преувеличено. Некоторые предпочитают искать и заменять литералы, чтобы внести свои изменения. Это хорошо, если 42 ясно означает смысл жизни и не смешивается с 42, атомным номером молибдена.

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


1
Семантика это ключ. Если и «А» имеет больше семантики, чем просто «А», то стоит связать одну семантику с одной и той же «ссылкой». Неважно, постоянная она или нет. Я абсолютно согласен.
oopexpert

-1

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

вспомнил его однообразную, вечную ясность, его ошеломительно разумные взгляды на все, его колоссальное удовлетворение трюизмами только потому, что они были правдой. "Смешайте все это!" закричал Тернбулл про себя: «Если он находится в убежище, то никого не может быть снаружи».

Г. К. Честертон, Бал и Крест

Нет ничего плохого в том, чтобы ценить правду, и нет ничего плохого в том, чтобы излагать истину, особенно при разговоре с компьютером.

Если вы будете лгать компьютеру, он получит вас

Перри Фаррар - Джермантаун, Мэриленд (от More Programming Pearls)


Но по большей части я согласен с людьми, которые говорят, что это глупо. Я слишком молод, чтобы научиться программировать на Фортране, но я слышал, как вы сказали, что вы можете переопределить 'A' = 'Q'и придумать всевозможные замечательные криптограммы. Вы этого не делаете.

Помимо проблем i18n, поднятых ранее (которые не переопределяют глиф «COMMA», но действительно переопределяют глиф DECIMAL_POINT). Создание цитат из французской моркови или британских одинарных кавычек, чтобы донести смысл до людей, - дело одно, и они действительно должны быть переменными, а не константами. Константа будет AMERICAN_COMMA := ','иcomma := AMERICAN_COMMA

И, если бы я использовал шаблон построителя для создания SQL-запроса, я бы предпочел увидеть

sb.append("insert into ")
 .append(table_name)
 .append(" values ")
 .append(" ( ")
 .append(val_1)
 .append(",")
 .append(val_2)
 .append(" ); ")

чем что-либо еще, но если бы вы собирались добавить константы, это было бы

INSERT_VALUES_START = " ( "
INSERT_VALUES_END = " ) "
INSERT_VALUES_SEPARATOR = " , "
QUERY_TERMINATOR = ";"

sb.append("insert into ")
 .append(table_name)
 .append(" values ")
 .append(INSERT_VALUES_START)
 .append(val_1)
 .append(INSERT_VALUES_SEPARATOR)
 .append(val_2)
 .append(INSERT_VALUES_END)
 .append(QUERY_TERMINATOR)

Однако, если вы когда-либо смотрели какую-либо другую программу (или тип), вы можете заметить некоторые интересные причуды. Не все из нас звездные машинистки. Многие из нас попали в программирование поздно или были воспитаны на советских клавиатурах (где клавиши пишут на вас), и нам нравится вырезать и вставлять отдельные буквы вместо того, чтобы пытаться найти их на клавиатуре и / или полагаться на автозаполнение.

Ничто не собирается автоматически заполнять строку для вас, поэтому, если я смогу получить запятую, нажав 'con', alt-space, down, down, down, введите и получите кавычку, нажав 'con', alt-space, down, вниз, введите. Я мог бы просто сделать это.


Еще одна вещь, которую нужно помнить о строковых литералах - это способ их компиляции. По крайней мере, в Delphi (это единственный язык, которым я зацикливался на стеке) вы свернете свои литералы, вставленные в стек каждой функции. Итак, много литералов = много служебных функций; «,» в function_A - это не тот же бит памяти, что и «,» в function_B ». Для борьбы с этим есть« строка ресурсов », которую можно построить и связать вбок - именно так они и делают вещи i18n (убивая две птицы с одного куста). в Python все ваши строковые литералы являются объектами, и это на самом деле может показаться хорошо использовать utils.constants.COMMA.join(["some","happy","array","strings"]), но это не звездная идея для точек повторяется снова и снова на этой странице.


-4

Но когда мы собираемся начать использовать символ, отличный от ',' для представления запятой?

Для локализации.

В англоязычных странах символом, разделяющим целые и дробные части десятичной дроби, является «.», Который мы называем «десятичной точкой». Во многих других странах символом является "," и обычно называется эквивалентом "запятая" на местном языке. Аналогичным образом, когда англоязычные страны используют «,» для разделения групп из трех цифр в большом количестве (например, 1 000 000 на один миллион), страны, которые используют запятую в качестве десятичной точки, используют точку (1 000 000).

Таким образом, есть возможность для создания констант DECIMAL_POINT и COMMA, если вы делаете глобализацию.


2
Но тогда COMMA и DECIMAL_POINT не являются правильными именами для сущностей (возможно, именно поэтому вы были отклонены).
Кайл Стрэнд

Вам нужно будет скомпилировать конкретные локализованные версии. Буквальные константы не подходят для этого; этот вариант использования будет вызывать файлы определений и искать их (что может включать константы, но не константы поиска, а константы поиска).
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.