Как мне избежать как глобальных переменных, так и магических чисел? - PullRequest
1 голос
/ 10 сентября 2010

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

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

doSomethingWithValue(1920);

Но это магическое число.Но чтобы избежать этого, я бы сделал ...

const int SCREEN_WIDTH = 1920;

//In a later file...
extern const int SCREEN_WIDTH;
doSomethingWithValue(SCREEN_WIDTH);

И теперь я использую глобальную переменную.Какое здесь решение?

Ответы [ 10 ]

11 голосов
/ 10 сентября 2010

Во втором примере SCREEN_WIDTH на самом деле не переменная 1 , это с именем константа . В использовании именованной константы нет ничего плохого.

В C вы можете использовать enum, если это целочисленная константа, потому что const-объект не является константой. В C ++ предпочтительнее использовать объект const, который вы использовали в исходном вопросе, потому что в C ++ объект const является константой.

1. Технически, да, это «переменная», но это имя на самом деле не «правильное», поскольку оно никогда не меняется.

5 голосов
/ 10 сентября 2010

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

2 голосов
/ 10 сентября 2010

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

Тогда вы просто берете это значение и передаете его туда, где оно необходимо.

Короче говоря, в этой строке: doSomethingWithValue(SCREEN_WIDTH); вы уже делаете это. SCREEN_WIDTH может быть глобальным в этом конкретном примере, но это не обязательно, потому что функция не обращается к ней как глобальная. Вы передаете значение функции во время выполнения, поэтому то, что она видит, не является глобальной переменной, это просто аргумент функции.

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

Глобальные константы обычно в порядке. Проблема возникает, когда у вас есть изменяемое глобальное состояние: объекты, к которым можно обращаться во всем приложении, и которые могут иметь различное значение в зависимости от при просмотре . Это затрудняет рассуждения и вызывает ряд проблем.

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

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

1 голос
/ 10 сентября 2010

Одним из способов избежать этого было бы настроить вашу программу как объект и иметь свойство объекта (my_prog.screen_width).Чтобы запустить вашу программу, создайте экземпляр объекта main () и вызовите метод -> go для объекта.

Java делает это.Много.Полуприличная идея.

Дополнительные функции для расширения вашей программы:

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

Это не большое дело для быстрого, хотя программа.

1 голос
/ 10 сентября 2010

Хотя глобальность в вашем втором случае довольно безобидна, если вы не разрабатываете ее для чего-то, где вы уверены ширина экрана не изменится, я бы использовал что-то для получения ширина экрана динамически (например, GetSystemMetrics в Windows или XDisplayWidth в X).

1 голос
/ 10 сентября 2010

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

Еще одна публикация, которая может помочь

1 голос
/ 10 сентября 2010

Это не переменная, это константа, которая разрешается во время компиляции.

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

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

1 голос
/ 10 сентября 2010

Они должны быть определены где-то. Почему бы не поместить определения в файл .h или в файл сборки?

1 голос
/ 10 сентября 2010

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

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

0 голосов
/ 10 сентября 2010

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

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

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

...