Большинство из нас говорят (часто даже не подозревая),
"Не использовать глобальные переменные"
Мартин Фаулер в одной из своих книг «Шаблоны архитектуры корпоративных приложений » пишет , что
"любая глобальная переменная всегда виновна, пока не доказано обратное"
- Почему использование глобальных переменных является плохой практикой?
- Они действительно вредны?
- Или это просто
иррациональнаяпредвзятая ненависть со стороны пуристов ?
Источник: mundogeek.net — глобальные переменные
Влияние изменений
Проблема с глобальными переменными заключается в том, что они создают скрытые зависимости. Когда дело доходит до больших приложений, вы даже не знаете/не помните/ясно не понимаете объекты, которые у вас есть, и их отношения.
Таким образом, вы не можете иметь четкого представления о том, сколько объектов используют вашу глобальную переменную. Что, если вы хотите что-то изменить в глобальной переменной, например, значение каждого из ее возможных значений или ее тип? Сколько классов или единиц компиляции затронет это изменение? Если сумма небольшая, возможно, стоит внести изменения. Если влияние будет большим, возможно, стоит поискать другое решение.
Но каково влияние? Поскольку глобальная переменная может использоваться в любой точке кода, ее измерение может быть очень сложным.
Кроме того, мы всегда стараемся, чтобы переменная имела как можно более короткое время жизни, чтобы объем кода, который использует эту переменную, был минимально возможным, и, таким образом, лучше понималось ее назначение и кто ее модифицирует.
Глобальная переменная существует в течение всего времени выполнения программы, и поэтому любой может использовать эту переменную либо для чтения, либо, что еще хуже, для изменения ее значения, что затрудняет определение того, какое значение переменная будет иметь в любой момент времени. данный пункт в программе.
приказ об уничтожении
Другая проблема — порядок уничтожения. Переменные всегда уничтожаются в порядке, обратном их созданию, независимо от того, являются ли они локальными или глобальными/статическими переменными (исключением являются примитивные типы,
int
,enum
s и т. д., которые никогда не уничтожаются, если они являются глобальными/статическими, до завершения программы).Проблема в том, что сложно узнать порядок построения глобальных (или статических) переменных. В принципе неопределенно.
Если все ваши глобальные/статические переменные находятся в одной единице компиляции (т. е. у вас есть только одна
.cpp
), то порядок сборки такой же, как и порядок записи (т. е. переменные, определенные первыми, создаются первыми).Но если у вас есть более одной,
.cpp
каждая со своими глобальными/статическими переменными, глобальный порядок сборки не определен. Конечно, порядок в каждой единице компиляции (каждой.cpp
) в частности соблюдается: если глобальная переменнаяA
определена раньшеB
,A
она будет построена раньшеB
, но переменные из других могут входитьA
иB
инициализироваться.cpp
. Например, если у вас есть три диска со следующими глобальными/статическими переменными:В исполняемом файле он может быть создан в этом порядке (или в любом другом порядке, если соблюдается относительный порядок внутри каждого
.cpp
):Почему это важно? Потому что, если есть отношения между разными статическими глобальными объектами, например, что они используют друг друга в своих деструкторах, возможно, в деструкторе глобальной переменной, вы используете другой глобальный объект из другой единицы компиляции, которая уже уничтожена ( были построены позже).
Скрытые зависимости и тестовые примеры
Я пытался найти шрифт, который собираюсь использовать в этом примере, но не могу его найти (в любом случае он должен был проиллюстрировать использование синглетонов, хотя пример применим к глобальным и статическим переменным). Скрытые зависимости также создают новые проблемы, связанные с управлением поведением объекта, если оно зависит от состояния глобальной переменной.
Представьте, что у вас есть платежная система, и вы хотите протестировать ее, чтобы увидеть, как она работает, учитывая, что вам нужно внести изменения, а код чужой (или ваш, но несколько лет назад). Вы открываете новый
main
, и вызываете соответствующую функцию вашего глобального объекта, предоставляющего услугу оплаты банковской картой, и оказывается, что вы вводите свои данные и они с вас снимают деньги. Как в простом тесте я использовал производственную версию? Как я могу сделать простой тест оплаты?После опроса других сотрудников выясняется, что вам нужно «установить значение true»,
bool
глобальное значение, указывающее, находимся ли мы в тестовом режиме или нет, прежде чем начинать процесс зарядки. Ваш объект, предоставляющий услугу оплаты, зависит от другого объекта, предоставляющего режим оплаты, и эта зависимость происходит незаметно для программиста.Другими словами, глобальные переменные (или синглтоны) делают невозможным переход в «тестовый режим», поскольку глобальные переменные не могут быть заменены «тестовыми» экземплярами (если вы не измените код, в котором создается или определяется указанный экземпляр). , но мы предполагаем, что тесты выполняются без изменения исходного кода).
Решение
Это решается тем, что называется внедрением зависимостей , которое заключается в передаче в качестве параметра всех зависимостей, которые нужны объекту в его конструкторе или в соответствующем методе. Таким образом, программист видит , что с ним должно произойти, поскольку он должен написать это в коде, что экономит разработчикам много времени.
Если слишком много глобальных объектов и слишком много параметров в функциях, которые в них нуждаются, вы всегда можете объединить свои «глобальные объекты» в класс в стиле фабрики , который создает и возвращает экземпляр (фиктивного) «глобального объекта». " вы хотите, передавая фабрику в качестве параметра объектам, которым требуется указанный глобальный объект в качестве зависимости.
Если вы переключаетесь в тестовый режим, вы всегда можете создать тестовую фабрику (которая возвращает разные версии одних и тех же объектов) и передать ее в качестве параметра, не изменяя целевой класс.
Но всегда ли это плохо?
Не обязательно, глобальные переменные могут быть полезны. Например, постоянные значения (значение PI). Будучи постоянным значением, нет риска не знать его значение в данной точке программы из-за любого типа модификации из другого модуля. Кроме того, постоянные значения имеют тенденцию быть примитивными, и их определение вряд ли изменится.
В этом случае удобнее использовать глобальные переменные, чтобы не передавать переменные в качестве параметров, упрощая сигнатуры функций.
Другим могут быть ненавязчивые «глобальные» службы, такие как класс ведения журнала (сохранение того, что происходит в файле, который обычно является необязательным и настраиваемым в программе и, следовательно, не влияет на основное поведение приложения) или
std::cout
,std::cin
илиstd::cerr
, которые также являются глобальными объектами.Все остальное, даже если его время жизни почти совпадает со временем жизни программы, всегда передается в качестве параметра. Переменная может быть даже глобальной в модуле, только в нем без доступа кого-либо еще, но в любом случае зависимости всегда присутствуют как параметры.
Глобальные переменные — плохая идея как минимум по 5 причинам:
Другая проблема заключается в том, что ужасно сложно отслеживать ваши изменения. Вполне возможно, что в какой-то момент вы создадите еще одну глобальную переменную с тем же именем и в конечном итоге перезапишете ее значение, не осознавая этого, что вызовет самые эзотерические ошибки и самые сложные для отладки.
В этом вопросе от сообщества разработчиков программного обеспечения у вас есть дополнительная информация:
https://softwareengineering.stackexchange.com/questions/148108/why-is-global-state-so-evil
Глобальные переменные — это области памяти, доступные любой части вашей программы или любой другой программе, работающей в том же контексте, что и ваше приложение, и, следовательно, также имеющей доступ к этой области памяти.
Это считается анти -шаблоном или плохой практикой по нескольким причинам. Здесь я упоминаю некоторые:
Ваша программа не будет легко рассуждать.
Естественный поток программы может быть потерян между многочисленными обновлениями ее зависимостей.
Сложность увеличивается
С большим количеством сторон, взаимодействующих на общей территории, вам теперь будет сложнее прогнозировать состояние вашего приложения в каждый момент времени. Это очень важно, чтобы он работал без ошибок.
Вы можете получить непредсказуемые результаты
Исходя из вышеизложенного, представьте, что у вас есть переменная со значением, и когда вы начинаете манипулировать ею, вы понимаете, что это не то, что вы ожидали, и что другая часть кода изменила указанное значение. Это может привести к тому, что ваша программа полностью выйдет из строя или сделает что-то, чего вы не хотите. Предсказуемое поведение является одним из требований к хорошей архитектуре.
создает неуверенность
Ваши данные не являются частными. Бывают ситуации, когда ваша программа имеет общий контекст с кодом, написанным кем-то другим. Классическим примером этого являются сторонние виджеты (твиттер, фейсбук и т. д.), которые включаются в веб-страницы. Эти сценарии выполняются в том же контексте, что и ваше приложение, и поэтому имеют доступ ко всем вашим глобальным переменным. Очевидно, что хранить какие-либо ценные данные в этой среде не рекомендуется из-за риска того, что они могут быть получены кем-то другим.
Кто-то другой может взломать ваш код
Исходя из вышеизложенного, может случиться так, что другой программист решит использовать то же имя переменной, что и вы (на самом деле это происходит очень часто). Поскольку они оба пытаются использовать ту же область памяти, что и их собственная, это приведет к сбою вашей программы. По этой причине, например, почти все библиотеки в Интернете имеют метод
noConflict
или аналогичный, позволяющий взаимодействовать между собой, не создавая конфликтов.Другие аргументы могут заключаться в том, что они нарушают ссылочную прозрачность и не помогают безопасности потоков , что очень затрудняет обнаружение условий гонки . Представьте себе, что два потока одной и той же программы пытаются записать значение в одно и то же место в одно и то же время.
Я могу придумать еще много, но я думаю, что аргумент вполне убедителен, поскольку все языки программирования имеют артефакты и методы, позволяющие избежать таких проблем с очень небольшими усилиями, поэтому нет необходимости попадать в такие проблемные шаблоны, если только это не является строго необходимым. . . .
Я могу привести вам пример, который поможет вам лучше понять:
Представьте, что у вас есть ящик с инструментами в общем доступе, и вам нужно закрутить винт, вы открываете ящик и не можете найти отвертку, или отвертка использована и сломана. Инструменты в коробке — это ваши глобальные переменные. В этих условиях вы никогда не сможете выполнять свою работу.
Некоторые проблемы с использованием глобальных переменных:
Что такое глобальные переменные?
Я считаю важным сначала иметь краткое представление о том, что такое глобальная переменная.
В компьютерном программировании глобальная переменная — это переменная с глобальной областью действия, что означает, что она видна (следовательно, доступна) во всей программе, если только она не скрыта. Набор всех глобальных переменных называется глобальной средой или глобальным состоянием. В компилируемых языках глобальные переменные обычно являются статическими переменными, длительность которых (время жизни) составляет все время выполнения программы , хотя в интерпретируемых языках (включая интерпретаторы командной строки) глобальные переменные обычно присваиваются динамически при объявлении, поскольку они не известны заранее.
En algunos lenguajes de programación, todas las variables son globales o globales por defecto, mientras que en la mayoría de los lenguajes de programación modernos las variables tienen un alcance limitado, generalmente un ámbito léxico, aunque las variables globales están a menudo disponibles declarando una variable en el nivel superior del programa. Sin embargo, en otras lenguajes no existen variables globales; estos son en general lenguajes de programación modulares que imponen una estructura de módulos o lenguajes de programación orientados a objetos basados en clases que imponen una estructura de clase.
¿Las variables globales son malas?
La respuesta es que sí, que casi siempre son malas. Aunque como sabemos, no hay en nada una regla que se cumpla al 100% y veremos por qué. Lo que sí debe quedar claro de entrada es que las variables globales deben evitarse cuando son innecesarias. Pero evitémosla usando la alternativa adecuada que ofrece el lenguaje de programación que estemos usando.
¿Por qué deben evitarse las variables globales cuando no sean estrictamente necesarias?
Cuando evitamos las globales, el código es generalmente más claro y más fácil de mantener, aunque como ya se ha dicho hay excepciones. Por el contrario su uso suele traer problemas muy serios.
Veamos solo algunos de los problemas serios que se presentan cuando usamos variables globales:
No localidad: el código fuente es más fácil de entender cuando el alcance de sus elementos individuales es limitado. Las variables globales pueden ser leídas o modificadas por cualquier parte del programa, por lo que es difícil recordar o razonar sobre cada posible uso.
Ningún control de acceso o comprobación de restricciones: una variable global puede ser obtenida o establecida por cualquier parte del programa y cualquier regla con respecto a su uso puede romperse o olvidarse fácilmente. (En otras palabras, los accesores get / set son generalmente preferibles sobre el acceso directo a datos, y esto es aún más importante para los datos globales.) Por extensión, la falta de control de acceso dificulta enormemente el logro de seguridad en situaciones en las que se desee ejecutar código no confiable (Como trabajar con complementos de terceros).
Acoplamiento implícito: Un programa con muchas variables globales a menudo tiene acoplamientos estrechos entre algunas de esas variables, y acoplamientos entre variables y funciones. La agrupación de elementos acoplados en unidades cohesivas generalmente conduce a mejores programas.
Problemas de concurrencia: Si las variables globales pueden ser accedidas por múltiples hilos de ejecución, la sincronización es necesaria (y demasiado a menudo se descuida). Al vincular dinámicamente los módulos con las globales, el sistema compuesto podría no ser seguro para los hilos incluso si los dos módulos independientes probados en docenas de contextos diferentes fueran seguros.
Contaminación del espacio de nombres: Los nombres globales están disponibles en todas partes. Usted puede, sin saberlo, terminar usando un global cuando usted piensa que está usando un local (por falta de ortografía u olvidarse de declarar el local) o viceversa. Además, si alguna vez tienes que vincular módulos que tienen los mismos nombres de variables globales, si tienes suerte, obtendrás errores de enlace. Si no tienes suerte, el enlazador tratará simplemente todos los usos del mismo nombre que el mismo objeto.
Problemas de asignación de memoria: Algunos entornos tienen esquemas de asignación de memoria que hacen difícil la asignación de globales. Esto es especialmente cierto en lenguajes donde los "constructores" tienen efectos secundarios distintos de la asignación (porque, en ese caso, se pueden expresar situaciones inseguras en las que dos globales dependen mutuamente). Además, al vincular dinámicamente módulos, puede no estar claro si diferentes bibliotecas tienen sus propias instancias de globales o si los globales se comparten.
Pruebas y Confinamiento: La fuente que utiliza globales es algo más difícil de probar porque no se puede establecer fácilmente un ambiente "limpio" entre las ejecuciones. De manera más general, la fuente que utiliza servicios globales de cualquier tipo (por ejemplo, lectura y escritura de archivos o bases de datos) que no se proporcionan explícitamente a esa fuente es difícil de probar por la misma razón. Para sistemas de comunicación, la capacidad de probar invariantes del sistema puede requerir que se ejecute más de una "copia" de un sistema simultáneamente, lo cual se ve enormemente obstaculizado por el uso de servicios compartidos -incluida la memoria global- que no se proporcionan para compartir como parte de la prueba.
Las pequeñas excepciones
Hay casos en los que la conveniencia de las variables globales supera los problemas potenciales mencionados anteriormente.
Imaginemos por ejemplo un programa muy pequeño o especial, especialmente del tipo 'plugin' en el que básicamente escribes un solo objeto o guión corto para un sistema más grande, usar globales puede ser lo más simple en este caso.
Cuando las variables globales representan instalaciones que realmente están disponibles en todo el programa, su uso simplifica el código.
Algunos lenguajes de programación no proporcionan soporte ni soporte mínimo para variables no globales.
Falsas alternativas al uso de globales o usarlas "creyendo" que no lo son
Algunas personas saltan a través de aros muy complicados para evitar el uso de globales. Muchos usos del SingletonPattern son apenas globales velados delgadamente. Si algo realmente debe ser global, hazlo global. No hagas algo complicado porque tal vez lo necesites algún día. Si existe una variable global, asumiría que se utiliza. Si se utiliza, hay métodos asociados con él. Colocar esos métodos en una sola clase y uno ha creado un singleton. Realmente es mejor especificar todas las reglas para el uso de una variable global en un lugar donde se pueden revisar por coherencia. El velo puede ser delgado, pero es valioso.
Incluso en los casos anteriores, es aconsejable considerar el uso de una de las Alternativas a Variables Globales para controlar el acceso a esta facilidad. Mientras que esto ayuda a prueba de futuro el código, por ejemplo, cuando su "pequeño" programa se convierte en uno muy grande - también puede simplificar problemas más inmediatos como probar el código o hacer que funcione correctamente en un entorno concurrente.
La declaración irreflexiva de variables se puede considerar un vicio de programación, en el cual podemos caer cuando empezamos a trabajar en un problema y cedemos a la tentación: "Necesito esa lista en muchos lugares diferentes. Declaro una Variable global ... y voila! ". Luego experimentamos que el número de globales comienza a ser inmanejable, entonces decidimos poner a todas las globales en una gran lista global de globales. A veces el globo explota cuando es demasiado tarde :)
Y es que el vicio ocurre porque agregar globales es muy fácil. Es fácil adquirir el hábito de declararlas. Es mucho más rápido que pensar en un buen diseño. Pero como se suele decir, lo barato sale caro. Es cierto que en algunas circunstancias simples, realmente la cosa más simple de hacer es usar una variable global. Pero cuando se trata de un programa complejo, una vez que se tiene una variable global, es mucho más difícil deshacerse de ella mientras pasa el tiempo y el programa crece. Nuestro código termina siendo dependiente de los posibles caprichos de dicha variable y arrojarnos resultados inesperados.
Realmente malas razones para usar variables globales
-¿Qué es una "variable local"? O sea, cuando no se entiende lo que es una variable local ni como funciona.
-¿Qué es un "miembro de datos"? Lo mismo, la ignorancia...
-"Soy una mecanógrafa lenta, los globales me guardan las pulsaciones de teclas". Pues ya ves :)
-"No quiero pasar parámetros todo el tiempo." No seas vago :)
-"No estoy seguro de a qué clase pertenecen estos datos, así que lo haré global." Vea las noticias, infórmese
Alternativas a variables globales
Las alternativas a variables globales son múltiples. Aunque es bueno señalar que escoger una alternativa supone a veces un paso delicado, en el sentido de que podemos estar optando por una alternativa que a lo mejor no tiene la capacidad de resolver esa situación concreta. Es el ejemplo típico mencionado más arriba en el caso de los SingletonPattern.
Veamos algunas de estas alternativas:
Objetos de Contexto: permiten agrupar y abstraer las dependencias globales y luego moverlas en un programa, funcionando efectivamente como variables globales pero mucho más fácil de anular y manipular localmente. Desafortunadamente, la mayoría de los lenguajes no ofrecen soporte para ContextObjects, que requiere "pasarlo todo el tiempo". Los hilos (Threading) de un ContextObject son ayudados por los alcances dinámicos (DynamicScoping) y las variables especiales (SpecialVariables).
Inyección de dependencia (DependencyInjection): La capacidad de configurar gráficos de objetos complejos en un programa reduce en gran medida la necesidad de pasar "variables" alrededor de las que llevan información global / de contexto. La fuerza de este enfoque es visible en paradigmas que hacen mucho menos uso de globales, como DataflowProgramming. Algunos lenguajes (como Java) tienen estructuras maduras de DependencyInjection que a menudo funcionan de manera algo estática (por ejemplo, a partir de un archivo de configuración, o no integran objetos vivos) y eso solo es suficiente para coincidir con muchos usos comunes de globales.
El soporte de primera clase (FirstClass) para DependencyInjection y la construcción de gráficos de flujo de datos o de objetos permite además componer sistemas complejos sobre la marcha en tiempo de ejecución (permitiendo un medio de composición para objetos primitivos que faltan en los lenguajes OO tradicionales) y además permite una enorme gama de optimizaciones, eliminación de códigos muertos, evaluación parcial, etc., lo que hace que esta sea una alternativa bastante atractiva a las globales.
Globales ocultas: las globales ocultas tienen un alcance de acceso bien definido e incluyen, por ejemplo, variables privadas 'estáticas' en clases y variables 'estáticas' en archivos '.c' y variables en espacios de nombres anónimos en C ++. Esta solución enjaula y localiza globales en lugar de domesticarlos - todavía se morderá cuando se trata de concurrencia y modularización y pruebas / confinamiento, pero al menos todos estos problemas serán localizados y fáciles de reparar, y no habrá problemas de vinculación .
Procedimientos de estado: Se trata de un conjunto global de setters y getters u operaciones que actúan sobre lo que es, implícitamente, el estado subyacente. Estos sufren muchos de los problemas asociados con los globales en lo que respecta a pruebas, concurrencia y asignación / intialización. Sin embargo, ofrecen un mejor control de acceso, la oportunidad de sincronización y una considerable capacidad de abstraer la implementación (por ejemplo, se podría poner el estado global en una base de datos).
SingletonPattern: Construye un objeto globalmente, permite el acceso a él a través de un procedimiento de estado. SingletonPattern ofrece la oportunidad conveniente para la especialización de una sola vez de un global basado en argumentos y el ambiente, y así puede servir bastante bien para abstraer los recursos que son verdaderamente parte del ambiente de programación (por ejemplo, monitores, altavoces, consola, etc.). Sin embargo, SingletonPattern no ofrece nunca la flexibilidad ofrecida por DependencyInjection o ContextObject, y está a la par con los procedimientos de estado en cuanto que ayudan al programador a controlar los problemas a los que los usuarios todavía se enfrentarán.
Base de datos o TupleSpace o DataDistributionService: A veces las variables globales se utilizan para representar datos. Esto es especialmente el caso de mapas globales, hashtables globales, listas globales. En menor grado, también es el caso de las colas de tareas. Para estos casos en los que los datos son realmente "globales", especialmente si cualquier parte del programa se dedica a empujar partes de estos datos globales a usuarios externos, el uso de un sistema dedicado simplificará en gran medida el programa y probablemente lo hará más robusto al mismo tiempo.
En Resumen
Usa las globales solamente cuando las puedas tener enjauladas (en un pequeño programa) y sean realmente necesarias en ese caso, o cuando no te quede ninguna otra alternativa.
« Плохую практику » использования глобальных переменных можно легко исправить. Он помещается в синглтон (плохая практика?), доступ к нему осуществляется с помощью синхронизированного геттера и сеттера , и — святое средство — больше не является плохой практикой. ;)
Хотя обычно рекомендуется максимально ограничить область действия переменной, бывают случаи, когда вам просто нужно сохранить глобальные переменные, потому что вы выполняете резервное копирование глобальных значений. Например, значение в базе данных ничем не отличается от глобальной переменной.
Таким образом, при использовании глобальной переменной следует применять те же соображения, что и при использовании значений базы данных (транзакции, атомарный доступ, локальные копии и т. д.).
Переменные глобальной области видимости были опасны только тогда, когда не было хороших способов защитить целостность их содержимого. Такие модификаторы
volatile
, как синхронизируемые геттеры и сеттеры , такие типы, как AtomicInteger , ... являются инструментами, которые позволяют вам придать переменной необходимую область действия.Плохая практика существует только тогда, когда разработчик не знает, что он делает и почему он это делает. Худшая практика — слепо следовать хорошим практикам, не понимая, почему, или теряться в попытках избежать предполагаемых плохих практик, которые могут быть лучшим способом решения конкретной проблемы.
Одни размышления...
Глобальные переменные имеют тенденцию вносить ошибки.
Если это глобальная переменная, ее можно изменить, иначе она была бы константой. Теперь, если его можно изменить, то его можно изменить из любого места в коде, поэтому вы не можете контролировать, кто изменяет его значение.
Отладка ошибки, которая может возникнуть откуда угодно, обычно вызывает головную боль.
Наконец, глобальные переменные не нужны. Не существует единственного варианта использования, который может быть охвачен исключительно глобальной переменной, а не какой-либо другой структурой. Например, в ООП у вас есть
Singleton
шаблон, который, хотя и не рекомендуется, но является допустимой альтернативой.Хороший способ понять опасность и вред глобальных переменных — рассмотреть типичный пример, когда они вызывают проблемы. Я собираюсь использовать C# для примеров, но этот принцип применим к любому языку программирования.
Возьмем следующий пример:
Скажем, меня попросили найти причину исключения, выданного выше, потому что это
mivariable
,null
хотя это никогда не предполагалось. В этом случае, посколькуmivariable
это не глобальная, а скорее переменная с четко определенным вводом метода (параметром), с помощью современных инструментов, таких как Visual Studio, довольно легко проследить поток вызовов метода, чтобы выяснить, в каком местеmivariable
испортился. Дело в том, что область действияmivariable
ограничена, поэтому мне не нужно исследовать бесчисленное количество мест в моем коде, где я мог изменить значениеmivariable
Напротив, что, если бы я исследовал ту же ошибку, но с
mivariable
определением ее как глобальной переменной:Теперь сложность моего расследования умножается, потому что я не только теперь должен найти все места, где могли манипулировать другие части кода
mivariable
, а я уверен, что их будет много, но я должен понять, в каком порядке все эти другие места выполняются для того, чтобы знать, в какое время делаются значенияmivariable
. Это огромная головная боль.Точно так же не только усложняется исследование проблемы, когда задействованы глобальные переменные, но по тем же причинам увеличиваются шансы внесения в код малозаметных недостатков из-за непонимания, где и когда используется переменная.
И, конечно же, чем сложнее проект и чем больше у вас кода, тем больше он становится реальной проблемой. Этот тип кода известен как спагетти-код из-за сложности его понимания.
И что хуже всего, нет никаких оснований для использования глобальных переменных. Обычно тот, кто их внедряет, делает это из лени и из желания сэкономить несколько строчек кода. Но долгосрочные затраты на это очень велики.
Учитель в школе сказал нам, что глобальные переменные очень легко доступны даже другим приложениям, такая ситуация значительно увеличивает риск потери или кражи информации злоумышленниками.
Переменная должна иметь минимально необходимую область действия. Я не думаю, что есть необоснованная ненависть к глобальным переменным. Имейте в виду, что наличие переменной глобальной области действия должно быть оправдано. Использование переменной (или синглтона) в правильном контексте не является анти-шаблоном. Проблема возникает, когда контекст переменной расширяется без необходимости. Это означает, что части кода могут получить доступ к переменным, за которые они не отвечают, либо из-за недостатка дизайна, либо из-за попытки повторного использования ресурса, что может привести к очень опасной проблеме двусмысленности в его использовании.
Нечто подобное происходит с плохой инкапсуляцией наших классов, поскольку мы можем предоставить чрезмерную видимость/доступ, влияя на еще большую зависимость, которая повлияет на нас в будущем обслуживании.