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

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

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

Итак, вопрос в следующем: не должны ли мы объявить джихад константам и использовать вместо этого все современные вещи, такие как DI, IoC или другие стильные слова?

3 ответа

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

class Foo
{
    public function doSomething()
    {
        if (ENV === ENV_DEV) {
            // do something this way
        } else {
            // do something that way
        }
    }
}

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

Вышесказанное также верно для констант, которые имеют только одно значение, например

public function log($message)
{
    fwrite(LOGFILE, $message);
}

Здесь константа будет указывать на файловый ресурс, определенный где-то снаружи как

define('LOGFILE', fopen('/path/to/logfile'));

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

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

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

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

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

class Environment
{
    private $value;

    public function __construct($value)
    {
        $this->assertValueIsAllowedValue($value);
        $this->value = $value;
    }

    public function getValue() {
// …

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

С другой стороны, вы также можете быть заинтересованы в:

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

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

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

Поэтому их можно использовать по всему миру.

Тем не менее, я видел некоторые плохо написанные PHP-код, который использует define создавать константы, но объявлять константы по-разному в разных обстоятельствах. Это неправильное использование констант: константа должна быть абсолютно фиксированной величиной; это должно быть только одно значение. Если у вас есть что-то, что может иметь разные значения в разных прогонах программы, это не должно быть определено как константа. Такие вещи действительно должны быть переменными, а затем должны следовать тем же правилам, что и другие переменные.

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

Существует большая разница между глобальной переменной и глобальной константой.

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

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

Синглтоны - это совсем другая проблема. Это часто используемый шаблон проектирования, который в итоге может стать объектно-ориентированной версией глобальных переменных. В некоторых языках (таких как C++) это также может пойти не так, если вы не следите за порядком инициализации. Тем не менее, иногда это может быть полезным паттерном, хотя обычно есть лучшие альтернативы (хотя иногда требуются немного больше работы).

РЕДАКТИРОВАТЬ: просто вкратце, вы упомянули в своем вопросе, что глобальные константы представляют "самое глобальное состояние когда-либо". Это не совсем точно, потому что глобальная константа (или должна быть) зафиксирована так же, как и исходный код. Он определяет статическую природу программы, в то время как "состояние" обычно понимается как динамическое понятие времени выполнения (т.е. то, что может меняться).

Другие вопросы по тегам