
Мы часто слышим: «Глобальные переменные — это плохо, избегайте их использования!» Но действительно ли это хороший совет? Упрощенные общие утверждения уже немного подозрительны, но если мы углубимся в детали этого, кажется, что он разваливается. В этой статье я рассматриваю время жизни и видимость переменных, необходимых для понимания утверждения. Это может оказаться просто бессмысленным заявлением.
Что такое глобальная переменная?
Термин «глобальный» не имеет единого определения для разных языков и архитектур. Эта непоследовательность требует от нас педантичности; если мне нужно вынести суждение о глобальных переменных, я должен понимать, о чем именно я говорю.
Переменные имеют несколько свойств, которые их определяют. Двумя ключевыми свойствами для нашего обсуждения являются время жизни и видимость. Тип переменной также играет роль, но он более тонкий, поэтому я не буду его здесь описывать.
Важно понимать разницу между именами и значениями. Некоторые языковые конструкции могут искажать ясность времени жизни и видимости, например указатели.
Продолжительность жизни
Время жизни переменной — это то, как долго существует значение (значение в некоторых языках называется «объектом»). Я не хочу слишком углубляться в эту концепцию, но нам нужен небольшой обзор.
Некоторые из распространенных жизней:
- временные: эти значения создаются выражениями, такими как
sin( a + 5 ). Полученное значение можно передать другой функции или скопировать в переменную. Промежуточные результаты исчезают, когда они больше не нужны, или в конце оператора в некоторых языках. - блок: переменные, объявленные внутри блочной области (разделы с тегами
{}илиbegin/endкакого-либо типа), как правило, имеют значения, которые существуют только внутри этого блока. - экземпляр: значения, определенные в классе, существуют в экземпляре класса. Это своего рода детская жизнь: когда умирает родитель, умирают и дети.
- application: эти значения имеют общее время жизни с приложением. Они создаются при ее запуске или, возможно, позже, и существуют до тех пор, пока программа не завершится.
- вручную: время существования значения явно контролируется с помощью
newиdeleteопераций.
Это классические варианты, если вы рассматриваете один исполняемый файл, написанный на одном языке. Было бы естественно пометить «глобальные переменные» как те, у которых есть время жизни приложения. Это может запутать, поскольку мы рассматриваем программные системы, содержащие более одной программы. У нас есть значения, которые сохраняются после выполнения: значения базы данных и конфигурации. Некоторые значения существуют до тех пор, пока работает хост-компьютер. В веб-приложении у нас есть время жизни сеанса: они исчезают при закрытии браузера. Непонятно, что означает глобальное время жизни.
Видимость
Видимость — это утверждение о том, как мы получаем доступ к значению. Типичный подход — через имя переменной. В этой ограниченной интерпретации есть несколько способов создания имен:
- область действия блока: имена внутри блока кода доступны только в этом блоке кода. Некоторые языки имеют «область действия функции» вместо произвольной области действия блока.
- частная область члена: имена внутри класса, доступные только функциям-членам.
- Область открытого члена: имена внутри класса, которые доступны до тех пор, пока у вас есть экземпляр класса.
- область модуля: весь исходный код в модуле видит эти имена. На самом деле они могут быть общедоступными и частными, как переменные-члены.
Некоторые значения «видимы» через функции доступа: сеттеры и геттеры. На первый взгляд, наличие функций get_a и set_a примерно эквивалентно общедоступной переменной a. Действительно, многие языки позволяют вам писать функции доступа непосредственно для общедоступной переменной. Здесь есть что-то немного тревожное; Я вернусь к этому.
Переменная также может быть помечена как доступная только для чтения с помощью тега const или final. Несмотря на имена, это не обязательно означает, что фоновый объект является постоянным, только то, что символ всегда указывает на один и тот же объект.
Если бы мы говорили о «глобальной» видимости, возможно, область видимости модуля была бы ближе всего. Однако эти переменные видны не везде, обычно внутри модуля или иногда экспортируются для общего пользования. Видимость тоже только вверх: пользователь модуля может видеть имена внутри него, но модуль не может видеть имена из кода более высокого уровня.
Корреляция и особенность
Некоторые из жизней и видимости имеют знакомые отношения:
- имя области действия блока обычно имеет значение с временем жизни блока
- имя области модуля обычно имеет значение с временем жизни приложения
Хотя это может быть значение по умолчанию, это не единственный вариант. Используя ключевое слово static или подобное ключевое слово, мы можем присвоить переменным области блока значение с временем жизни приложения. Одно и то же ключевое слово может также создавать свойства-члены, которые имеют время жизни приложения: они называются «переменными класса».
Если мы занимаемся многопоточным программированием, мы используем «локальное время жизни потока». Эти переменные могут быть видны всему модулю, но каждый запущенный поток имеет связанное с ним отдельное значение.
Вернемся к упомянутым мной функциям доступа. Если доступ к значению скрыт за функциями, у вызывающей стороны нет реального способа узнать время жизни резервного значения. Эти средства доступа должны играть роль в любом совете, который мы даем по «глобальным» значениям.
Эм, так что тут хорошего или плохого?
Все это возвращает нас к исходному вопросу: плохи ли глобальные переменные? Мы не могли ответить раньше, потому что у нас не было четкого определения. Теперь, вооружившись нашими знаниями о видимости и доступности, я не уверен, что мы хотим придумать определение.
Должна ли глобальная переменная быть определена как переменная с областью действия модуля и временем жизни приложения? Имеет ли значение, является ли он приватным для модуля? Если он изменяется только с помощью функций доступа, остается ли он глобальным? Учтите, что такая функция, как get_time(), аналогична переменной только для чтения, обращающейся к текущему времени, и мне трудно поверить, что мы хотим сказать, что эта функция «плохая».
Что, если моя программа представляет собой микросервисную архитектуру? У меня есть несколько небольших программ, которые запускаются и останавливаются с частыми интервалами. Хотя технически у меня есть много значений времени жизни приложения, оно кажется более ограниченным из-за того, как я их использую. Они определенно не являются «глобальными» в моей системе.
Ответ
Я не думаю, что могу дать удовлетворительное определение «глобальной переменной», которое имело бы хоть какую-то универсальную полезность. Это означало бы, что несколько бессмысленно спрашивать «плохие ли глобальные переменные?» Вопрос должен быть более тонким, чем это. Оно должно относиться к применимости всех жизней и видимостей.
Отвечая на вопрос «Какова применимость различных комбинаций видимости имени и времени жизни значения?» будет долгая и сложная дискуссия. Короче говоря, уверяю вас, что все комбинации имеют как хорошее, так и плохое применение.
Первоначально опубликовано на сайте mortoray.com 15 ноября 2017 г.