Строковые константы переоценены? - PullRequest
4 голосов
/ 04 июня 2009

Легко потерять нечетные числа, такие как 0, 1 или 5. Раньше я очень строго относился к этому, когда писал низкоуровневый C-код. Поскольку я больше работаю со всеми строковыми литералами, связанными с XML и SQL, я часто нарушаю правило встраивания констант в код, по крайней мере, когда речь идет о строковых литералах. (Я все еще хорошо разбираюсь в числовых константах.)

Строки не совпадают с числами. Создавать постоянную времени компиляции, имя которой совпадает с ее значением (например, const string NameField = "Name";), кажется утомительным и немного глупым, и хотя повторение одного и того же строкового литерала во многих местах кажется рискованным, вероятность опечатки мала благодаря копированию и вставке, и когда я занимаюсь рефакторингом, я обычно выполняю глобальный поиск, который включает изменение не только названия вещи, например, как она обрабатывается функционально по отношению к окружающим вещам.

Итак, допустим, у вас нет хорошего сериализатора XML (или вы не готовы его настроить). Какой из них вы бы лично использовали (если бы вы не пытались преклониться перед давлением сверстников в каком-то обзоре кода):

static void Main(string[] args)
{
    // ...other code...

    XmlNode node = ...;

    Console.WriteLine(node["Name"].InnerText);
    Console.WriteLine(node["Color"].InnerText);
    Console.WriteLine(node["Taste"].InnerText);

    // ...other code...
}

или

class Fruit
{
    private readonly XmlNode xml_node;

    public Fruit(XmlNode xml_node)
    {
        this.xml_node = xml_node;
    }

    public string Name
    { get { return xml_node["Name"].InnerText; } }

    public string Color
    { get { return xml_node["Color"].InnerText; } }

    public string Taste
    { get { return xml_node["Taste"].InnerText; } }
}

static void Main(string[] args)
{
    // ...other code...

    XmlNode node = ...;
    Fruit fruit_node = new Fruit(node);

    Console.WriteLine(fruit_node.Name);
    Console.WriteLine(fruit_node.Color);
    Console.WriteLine(fruit_node.Taste);

    // ...other code...
}

Ответы [ 9 ]

11 голосов
/ 04 июня 2009

Определенную константу проще перестроить. Если «Имя» используется три раза и вы меняете его на «Полное имя», изменение константы - это одно изменение вместо трех.

5 голосов
/ 04 июня 2009

Для чего-то подобного это зависит от того, как часто используется константа. Если это только в одном месте, как в вашем примере, то жесткое кодирование - это хорошо. Если он используется во многих разных местах, обязательно используйте константу. Одна опечатка может привести к часам отладки, если вы не будете осторожны, потому что ваш компилятор не заметит, что вы ввели «Tsate» вместо «Taste», тогда как он заметит, что вы ввели fruit_node.Tsate вместо fruit_node. Вкус.

Edit: Теперь я вижу, что вы упомянули о копировании и вставке, но если вы делаете это, вы также можете терять время, которое вы экономите, не создавая константу в первую очередь. С помощью intellisense и автозаполнения вы можете получить константу всего за несколько нажатий клавиш, вместо того чтобы копировать / вставлять.

0 голосов
/ 16 декабря 2013

Да, в значительной степени.

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

var x = myObject.prop1.prop2;

Эквивалентно этому:

var x = window["myObject"]["prop1"]["prop2"]; // assuming global scope

Но это определенно не стандартная практика в JavaScript, чтобы сделать это:

var OBJ_NAME = "myObject";
var PROP1_NAME = "prop1";
var PROP2_NAME = "prop2";

var x = window[OBJ_NAME][PROP1_NAME][PROP2_NAME];

Это было бы просто смешно.

Это все еще зависит, хотя, например, если строка используется во многих местах и ​​ее довольно громоздко / некрасиво набирать («имя» против «my-custom-property-name-x»), то, вероятно, стоит сделать константа, даже в пределах одного класса (в этот момент, вероятно, хорошо быть внутренне непротиворечивым внутри класса и сделать все остальные строковые константы тоже).

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

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

Одна вещь, которую никто, казалось, не заметил, это то, что как только вы определяете константу, ее объем становится чем-то, что нужно поддерживать и обдумывать. Это на самом деле имеет стоимость, это не бесплатно, как кажется, все думают. Учтите это:

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

Обычно эти "великие рефакторинги" никогда не происходят, и когда они это делают, они все равно требуют полного переписывания со всеми новыми строками. И если вы использовали какой-то общий модуль до этого великого рефакторинга (как в предыдущем абзаце), у которого не было этих новых строк, которые вам сейчас нужны, что тогда? Добавляете ли вы их в один и тот же общий модуль констант (что если у вас нет доступа к коду для этого общего модуля)? Или вы оставляете их локальными для вас, и в этом случае теперь существует несколько разбросанных репозиториев строковых констант, все на разных уровнях, с риском дублирования констант по всему коду? Как только вы дойдете до этой точки (и поверьте мне, что я видел это), рефакторинг станет спорным, потому что пока вы получите все ваши использования ваших констант, вы пропустите другие использования людьми их констант, даже если эти константы имеют то же логическое значение, что и ваши константы, и вы фактически пытаетесь изменить их все.

0 голосов
/ 05 июня 2009

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

0 голосов
/ 04 июня 2009

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

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

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

0 голосов
/ 04 июня 2009

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

0 голосов
/ 04 июня 2009

Лично я бы заранее загрузил фрукты из файла XML - что-то вроде:

public class Fruit
{
    public Fruit(string name, Color color, string taste)
    {
        this.Name = name;  this.Color = color; this.Taste = taste;
    }

    public string Name { get; private set; }
    public Color Color { get; private set; }
    public string Taste { get; private set; }
}

// ... In your data access handling class...
    public static FruitFromXml(XmlNode node) 
    { 
            // create fruit from xml node with validation here 
    }
}

Таким образом, «фрукты» на самом деле не привязаны к хранилищу.

0 голосов
/ 04 июня 2009

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

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

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

0 голосов
/ 04 июня 2009

Как вы, наверное, догадались. Ответ: это зависит от контекста.

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

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

...