106

La mayoría de nosotros decimos, (muchas veces sin saber realmente),

“No utilizar variables globales”


Martin Fowler expresa en unos de sus libros, Patterns of Enterprise Application Architecture, que

“cualquier variable global es siempre culpable hasta que se demuestre lo contrario”

  • ¿Por qué es una mala practica usar variables globales?
  • ¿Realmente son perjudiciales?
  • ¿O se trata sólo de un odio irracional prejuicios por parte de los puristas?

Fuente: mundogeek.net - Variables globales

Black Sheep
  • 13,096
  • 6
  • 36
  • 60
  • 7
    ¿malvado? ¿odiadas? ¿puristas?... ¿ esto es una pregunta tecnica ? – rnrneverdies Oct 21 '16 at 15:00
  • 7
    Lamento ser quien hace esta observacion, pero las respuestas a tu pregunta requieren que esten basadas en una opinión... por lo que esta pregunta deberia ser cerrada ... ¿por qué? por tienden a atraer trafico no deseado al sitio y a tu pregunta (SPAM, etc.) – Rafael Oct 21 '16 at 16:22
  • Las variables globales tienen sus usos, el mas frecuente es el de conservar constantes o literales que no cambiaran en mucho tiempo. Por ejemplo la configuración de conexión a una base de datos. – IgniteCoders Oct 21 '16 at 16:45
  • 15
    @Rafael - Por una parte tienes razón, pero en SO - Español, de momento, se pueden hacer preguntas que normalmente pertenecen a http://programmers.stackexchange.com/ - Para más información: [Definición de la temática apropiada para el sitio](http://meta.es.stackoverflow.com/a/163/6491) – Black Sheep Oct 21 '16 at 16:59
  • 3
    Aunque hay muchas razones para ver las variables globales (¿singleton, alguien? como un problema, lastimosamente tienen sus beneficios **muy limitados**. Tal es el caso de: variable sistema operativo (Windows, Linux, etc), variable spool de impresión, variable espacio vacío (/dev/null), y otras que son únicas per se. De preferencia, no las declares a menos que sea estrictamente necesario (y aún así, dudo que realmente sea necesario). –  Oct 21 '16 at 21:55
  • 2
    Si alguna ves das mantenimiento a un sistema que no hayas hecho tu y que maneje variables globales te darás cuenta lo difícil que es lidiarlas, puede volverse muy difícil tener certeza del valor de las mismas. – AlejandroMedina Nov 07 '16 at 17:26
  • 2
    **se basa principalmente en opiniones ... debe ser cerrada. – Jorgesys Nov 24 '16 at 16:35
  • @aldanux Pregunta se basa a opiniones, pero bueno es un buen tema el que tengo curiosidad porque mencionan que es una mala practica utilizar variables globales. ¡Pero! ¿Porque utilizas un libro con una ruta de afiliado? – Otto Aug 26 '17 at 13:05

15 Answers15

112

Impacto del cambio

El problema de las variables globales es que crea dependencias ocultas. Cuando se trata de aplicaciones grandes, ni tú mismo sabes/recuerdas/tienes claro los objetos que tienes y sus relaciones.

Entonces, no puedes tener una noción clara de cuántos objetos están usando tu variable global. ¿Y si quieres cambiar algo de la variable global, por ejemplo, el significado de cada uno de sus valores posibles, o su tipo? ¿A cuántas clases o unidades de compilación afectará dicho cambio? Si la cantidad es pequeña, quizás merezca la pena realizar el cambio. Si el impacto será grande, quizás merezca la pena buscar otra solución.

Pero, ¿cuál es el impacto? Debido a que una variable global puede ser usada en cualquier punto del código, puede llegar a ser muy difícil medirlo.

Además, siempre se intenta que una variable tenga el tiempo de vida más corto posible, para que la cantidad de código que hace uso de esa variable sea el mínimo posible, y así entender mejor su propósito, y quién lo modifica.

Una variable global dura lo que dure el programa, y por tanto, cualquiera puede hacer uso de la variable, bien para leerla, o aún peor, para cambiar su valor, haciendo más difícil saber qué valor tendrá la variable en cualquier punto del programa dado.

Orden de destrucción

Otro problema es el orden de destrucción. Las variables siempre se destruyen en orden inverso a su creación, sean variables locales o globales/estáticas (una excepción son los tipos primitivos, int, enums, etc, que nunca se destruyen si son globales/estáticos hasta que acaba el programa).

El problema, es que es difícil saber el orden de construcción de las variables globales (o estáticas). En principio, es indeterminado.

Si todas tus variables globales/estáticas están en una sola unidad de compilación (es decir, que solo tengas un .cpp), entonces el orden de construcción es el mismo que el de escritura (es decir, variables definidas antes, se construyen antes).

Pero si tienes más de un .cpp cada uno con sus propias variables globales/estáticas, es indeterminado el orden de construcción global. Eso sí, el orden en cada unidad de compilación (cada .cpp) en particular, se respeta: si la variable global A va definida antes que B, A se construirá antes que B, pero puede que entre A y B se inicialicen variables de otros .cpp. Por ejemplo, si tienes tres unidades con las siguientes variables globales/estáticas:

introducir la descripción de la imagen aquí

En el ejecutable podría crearse en este orden (o en cualquier otro orden siempre que se respete el órden relativo dentro de cada .cpp):

introducir la descripción de la imagen aquí

¿Por qué esto es importante? Porque si hay relaciones entre los diferentes objetos globales estáticos, por ejemplo, que unos usen a otros en sus destructores, quizás, en el destructor de una variable global, utilizas otro objeto global de otra unidad de compilación que resulta que ya está destruído (al haberse construído después).

Dependencias ocultas y test cases

He intentado buscar la fuente que voy a utilizar en este ejemplo, pero no la encuentro (de todas formas, era para ejemplificar el uso de singletones, aunque el ejemplo es aplicable a variables globales y estáticas). Las dependencias ocultas también crean nuevos problemas relacionados con controlar el comportamiento de un objeto, si éste depende del estado de una variable global.

Imagina que tienes un sistema de pago, y quieres probarlo para ver cómo funciona, dado que necesitas hacer cambios, y el código es de otra persona (o tuyo, pero de hace unos años). Abres un nuevo main, y llamas a la función correspondiente de tu objeto global que provee un servicio de pago bancario con tarjeta, y resulta que introduces tus datos y te hacen un cobro. ¿Cómo, en una simple prueba, he utilizado una versión de producción? ¿Cómo puedo hacer un simple test de pago?

Después de preguntar a otros compañeros de trabajo, resulta que tienes que "marcar a true", un bool global que indica si estamos en modo de pruebas o no, antes de comenzar el proceso de cobro. Tu objeto que provee el servicio de pago depende de otro objeto que provee el modo de pago, y esa dependencia ocurre de manera invisible para el programador.

En otras palabras, las variables globales (o singletones), hacen imposible pasar a "modo test", dado que las variables globales no se pueden sustituir por instancias "de testeo" (a no ser que modifiques el código donde se crea o define dicha variable global, pero asumimos que los test se hacen sin modificar el código madre).

Solución

Ésto se soluciona mediante lo que se llama dependency injection, que consiste en pasar como parámetro todas las dependencias que un objeto necesite en su constructor o en el método correspondiente. De esta manera, el programador lo que tiene que pasarle, dado que tiene que escribirlo en código, haciendo ganar mucho tiempo a los desarrolladores.

Si hay demasiados objetos globales, y hay demasiados parámetros en las funciones que las necesitan, siempre puede agrupar tus "objetos globales" en una clase, estilo factoría, que construya y devuelva la instancia del "objeto global" (simulado) que quieres, pasando la factoría como parámetro a los objetos que necesiten dicho objeto global como dependencia.

Si pasas a modo test, siempre puedes crear una factoría de testeo (que devuelva versiones diferentes de los mismos objetos), y pasarla como parámetro sin tener que modificar la clase destino.

¿Pero siempre es malo?

No necesariamente, puede haber buenos usos para variables globales. Por ejemplo, valores constantes (el valor de PI). Al ser un valor constante, no hay riesgo de no saber su valor en un punto dado del programa por cualquier tipo de modificación desde otro módulo. Además, los valores constantes tienden a ser primitivos y es poco probable que cambie su definición.

Es más cómodo, en ese caso, hacer uso de variables globales para no tener que estar pasando las variables como parámetros, simplificando las firmas de las funciones.

Otro pueden ser servicios "globales" no intrusivos, como una clase de logging (guardar en un fichero lo que pasa, cosa que suele ser opcional y configurable en un programa, y por tanto, no afecta al comportamiento nuclear de la aplicación), o std::cout, std::cin o std::cerr, que también son objetos globales.

Cualquier otra cosa, aunque su tiempo de vida coincida casi con el del programa, pasalo siempre como parámetro. Incluso, la variable podría ser global en un módulo, solo en él sin que ningún otro tenga acceso, pero que, en cualquier caso, las dependencias estén presentes siempre como parámetros.

achecopar
  • 111
  • 6
Peregring-lk
  • 2,499
  • 1
  • 8
  • 11
  • 9
    Como nota, las constantes no son variables, son constantes. – rnrneverdies Nov 20 '16 at 01:15
  • La pregunta no es sobre C++, es conceptual. – rnrneverdies Nov 20 '16 at 12:08
  • @Peregring-lk ¿A que te refieres con objetos globales? me imagino que refieres a variables globales... pero si se esta diciendo que es una mala practica usar variables globales, creo que te estas contradiciendo o yo estoy entendiendo mal? Cuando te refieres a dependencia injection como lo solucionarias si un formulario padre llama al hijo y este llama a otro formulario y desde este ultimo se tiene que tomar datos y pasarselo al padre? – Pedro Ávila Apr 15 '17 at 00:58
  • @PedroÁvila En C++ (lo siento, estoy acostumbrado a utilizar el vocabulario de C++, aunque esta pregunta sea genérica), un objecto es una región de memoria. En una variable, aunque sea de tipo primitivo, por ejemplo `int a`, `a` es el nombre de un objecto de tipo `int`. Es decir, la entidad designada por el nombre `a`, es un objecto porque ocupa memoria. Por ejemplo, en `int fun();`, la entidad designada por `fun`, no es un objecto porque las funciones "no ocupan memoria". En otros lenguajes, objectos son solo las instancia de clase, pero en C++ un objecto es cualquier cosa que ocupe memoria. – Peregring-lk Apr 15 '17 at 11:53
  • @PedroÁvila me estoy contradiciendo donde? Y respecto a "como notificar al padre", cuando el formulario hijo llama al formulario nieto, que el resultado se devuelva en el retorno de la función, y el hijo devuelva al padre lo que el nieto le devolvió a él, o que el resultado se guarde en el objecto en sí, y luego con una serie de funciones estilo "getName", "getBirth", se puedan consultar los valores guardados en el formulario, o que el padre se pase por referencia al hijo, y éste al nieto, para que el nieto le diga al abuelo los resultados finales. – Peregring-lk Apr 15 '17 at 11:59
  • @PedroÁvila En fin, el caso es que se vea siempre quién llama a quién o quién usa qué. – Peregring-lk Apr 15 '17 at 11:59
40

Las variables globales son mala idea por, al menos, 5 razones:

  • El código es más difícil de entender
  • El código es más difícil de depurar
  • El código es más difícil de testear
  • El código es más difícil de mantener
  • El código es más difícil de reutilizar

Otro problema es que es terriblemente difícil seguir sus cambios. Es posible que en algún momento crees otra variable global con el mismo nombre, y termines sobre escribiendo su valor sin percatarte, lo cuál generaría errores de lo más esotéricos y de lo más difíciles de depurar.

En esta pregunta de la comunidad Software Engineering tienes más información:

https://softwareengineering.stackexchange.com/questions/148108/why-is-global-state-so-evil

Juan Pinzón
  • 9,007
  • 18
  • 48
  • 76
28

Las variables globales son espacios de memoria accesibles por cualquier parte de tu programa o cualquier otro programa ejecutándose en el mismo contexto de tu aplicación y por ende que tenga también acceso a dicho espacio de memoria.

Esto es considerado un antipatrón o mala práctica por varias razones. Aquí te menciono algunas:

  1. Tu programa no será sencillo de razonar.

    El flujo natural del programa se puede perder entre las múltiples actualizaciones de sus dependencias.

  2. Aumenta la complejidad

    Con un mayor número de partes interactuando en un área común ahora te será más difícil predecir el estado de tu aplicación a cada momento. Esto es muy importante para que funcione sin errores.

  3. Puedes tener resultados impredecibles

    Derivado de lo anterior, imagina que tienes una variable con un valor y cuando vas a manipularlo te das cuenta que no es el que esperabas y es que otra parte del código alteró dicho valor. Esto puede causar que tu programa falle completamente o que haga algo que no deseas que haga. Que sea comporte de manera predecible es uno de los requisitos para tener una buena arquitectura.

  4. Crea inseguridad

    Tus datos no son privados. Hay situaciones en las que tu programa comparte contexto con código escrito por alguien más. Un ejemplo clásico de esto son los widgets de terceros (twitter, facebook, etc) que se incluyen en las páginas web. Dichos scripts se ejecutan en el mismo contexto de tu aplicación y por ende tienen acceso a todas tus variables globales. Evidentemente no sería buena idea almacenar algun dato valioso en ese entorno por el riesgo a que este pueda ser obtenido por alguien más.

  5. Alguien más puede romper tu código

    Derivado de lo anterior puede ser que a otro programador se le ocurra usar el mismo nombre de variable que a tí (esto de hecho ocurre muy a menudo). Debido a que ambos estan intentando usar la misma región de memoria como si fuera propia esto causará que tu programa deje de funcionar. Esta es la causa de que, por ejemplo, casi todas las librerías en la web cuenten con un método noConflict o similar para poder interoperar entre ellas sin crear conflictos.

Otros argumentos pueden ser que violan la transparencia referencial y no ayudan a la seguridad entre hilos, creando condiciones de carrera muy difíciles de detectar. Imagina a dos hilos de un mismo programa intentando escribir un valor en el mismo lugar al mismo tiempo.

Se me ocurren muchas más, pero creo que el argumento es bastante convincente ya que todos los lenguajes de programación tienen artefactos y técnicas para evitar dichos problemas con muy poco esfuerzo, así que no es necesario caer en patrones tan problemáticos a menos que sea estrictamente necesario.

Te puedo poner un ejemplo que te ayudará a comprender mejor:

Imagina que tienes una caja de herramientas en un área de acceso común y necesitas apretar un tornillo, abres la caja y no encuentras el destornillador o el destornillador está usado y roto. Las herramientas de la caja son tus variables globales. En estas condiciones nunca lograrás hacer tu trabajo.

Mauricio Arias Olave
  • 3,098
  • 6
  • 26
  • 55
devconcept
  • 12,541
  • 3
  • 39
  • 56
24

Algunos de los problemas que presenta usar variables globales:

  • Pueden ser modificadas en cualquier parte del programa y por cualquier función, lo cual puede ser una pesadilla a la hora de depurar el código si ocurre un error.
  • Corres el riesgo de redefinirlas y perder los valores que tenías (aunque esto puede ocurrir de manera local también, mientras mayor sea el ámbito de una variable, mayor será la posibilidad de que el programador se olvide que ya hay una variable con ese nombre en ese ámbito).
  • En algunos lenguajes, son más lentas porque se debe buscar su valor en un ámbito (scope) mayor. Por ejemplo, el motor de JavaScript atravesará todos los ámbitos hasta encontrar la variable, siendo las globales las últimas. Realmente la diferencia será mínima, pero algo habrá.
Juan Pinzón
  • 9,007
  • 18
  • 48
  • 76
Alvaro Montoro
  • 48,157
  • 26
  • 100
  • 179
  • Son más lentas depende del lenguage, en C no habria ninguna diferencia. – rnrneverdies Oct 21 '16 at 15:14
  • 1
    @rnd: Lo mismo iba a comentar. Si es un lenguaje "compilado", entonces no hay deferencia cuando el código se ejecuta. – sstan Oct 21 '16 at 15:15
  • Gracias por los comentarios, he añadido una pequeña nota al respecto – Alvaro Montoro Oct 21 '16 at 15:15
  • Otro detalle: el segundo punto también depende del lenguaje de programación. En muchos lenguajes populares es ilegal redefinir una variable y resulta en un error de compilación, de modo que no existe ese riesgo en esos casos. – sstan Oct 21 '16 at 16:40
  • Creo que en Javascript, toda función, que también es un objeto, tiene "bindado" todas las variables en su contexto. No creo que haya que hacer "dicha búsqueda", sino que, sencillamente, cuando la función se ejecuta, tiene en su contexto referencias a todos los objetos que se utilizan dentro de la función. Supongo que el impacto negativo en ejecución será a la hora de construir dicho contexto antes de llamar a la función. No se cómo se optimizarán las construcciones de estos contextos, pero no creo que "se busque nada" en el momento de utilizar la variable. – Peregring-lk Nov 20 '16 at 02:26
19

¿Qué son las variables globales?

Considero importante antes tener un concepto breve de lo que es una variable global.

En la programación de computadoras, una variable global es una variable con alcance global, lo que significa que es visible (por lo tanto accesible) en todo el programa, a menos que esté oculta. El conjunto de todas las variables globales se conoce como el entorno global o el estado global. En los lenguajes compilados, las variables globales son generalmente variables estáticas, cuya extensión (lifetime) es toda la ejecución del programa, aunque en los lenguajes interpretados (incluidos los intérpretes de línea de comandos), las variables globales generalmente se asignan dinámicamente cuando se declaran, ya que no se conocen antes de tiempo. introducir la descripción de la imagen aquí

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.

Inyección de dependencia (Dependency injection)

  • 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.

A. Cedano
  • 86,578
  • 19
  • 122
  • 221
  • Buenísima respuesta. Es cierto que las globales son necesarias, y en muchos casos! Una BD o la configuración del programa, por ejemplo. La dependency injection puede ser muy pesada, incluso cuando solo necesitas 1 global. Pero si las necseitas, en mi opinión, deben tener varias características: ser fácilmente "grep-eables" (buscar con grep donde se están utilizando), substituibles (falsear durante un breve periodo de tiempo la variable y luego restaurar su valor), y que no se autoinicialicen: que tenga que crear la instancia e instalarla como global, desde el main por ejemplo. – Peregring-lk Apr 09 '17 at 14:11
14

La "mala practica" de usar variables globales se puede solucionar fácilmente. Se coloca en un Singleton (¿mala práctica?), se accede con getter y setter sincronizado y - santo remedio - ya no es mala práctica. ;)

Aunque es generalmente recomendable limitar el alcance de una variable lo más que se pueda, hay casos en que simplemente se necesita guardar variables globales porque se están respaldando valores globales. Por ejemplo, un valor en una base de datos no es distinto a una variable global.
Así, usando una variable global se deberían aplicar las mismas consideraciones que se aplican cuando con el uso de valores de de base de datos (transacciones, atomizar acceso, copias locales, etc).

Las variables de alcance global solamente eran peligrosas cuando no habían buenas formas de proteger la integridad de su contenido. Modificadores como volatile, getters y setters sincronizables, tipos como AtomicInteger, ... son herramientas que permiten dar a una variable el alcance que necesita.

Las malas practicas solamente existen cuando el desarrollador no sabe lo que hace ni por qué lo hace. La peor practica es seguir ciegamente buenas prácticas sin entender el por qué, o perderse en evitar presuntas malas prácticas que podrían ser la mejor forma de solucionar un problema en particular.

VRoxa
  • 3,040
  • 6
  • 19
Stefan Nolde
  • 6,648
  • 1
  • 24
  • 44
  • Pero un Singleton en resumida es una clase que tiene una única instancia, en ves de tener una variable global digamos un string ahora tenes una clase global, lo que pasando de ser un acoplamiento de datos se convirtió en un acoplamiento de contenido. – Ricardo D. Quiroga Mar 30 '17 at 13:42
  • Muy cierto. Tal cual como variables globales uno los usa cuando es necesario por razones de arquitectura o lógica, pero generalmente lo tratas de evitar. La ventaja de encapsular variables globales en un Singleton es tener más control sobre lo que pasas en el acceso, por ejemplo puedes pasar una copia, o puedes asegurarte que si hay una relación entre dos variables globales, que los actualizas en forma atómica si uno cambia. Lo mismo vale para tipos atómicos para concurrencia. En síntesis - nada es malo si no tienes buenas alternativas. – Stefan Nolde Mar 30 '17 at 14:19
12

Algunas reflexiones...

Las variables globales tienden a introducir errores.

Si es una variable global, esta puede modificarse, si no fuese así seria una constante. Ahora, si puede modificarse, entonces puede modificarse desde cualquier parte del código, por lo tanto no se puede controlar quien modifica su valor.

Depurar un error que puede provenir de cualquier parte suele ser un dolor de cabeza.

Finalmente, Las variables globales no son necesarias. No existe un solo caso de uso que pueda ser cubierto exclusivamente por una variable global y no por alguna otra estructura. Por ejemplo, en OOP tienes Singleton, un pattern que, aunque no es muy recomendado tampoco, es una alternativa valida.

rnrneverdies
  • 16,491
  • 3
  • 49
  • 79
  • 3
    Un _Singleton_ no es recomendado hasta que se necesita uno. Eso vale para todos patrones en el desarrollo de software. – Stefan Nolde Jan 08 '17 at 05:20
  • Estoy de acuerdo, pero quisiera añadir que en python para hacer una modificación por asignación de cualquier variable global es necesario incorportar al inicio de la función la sentencia `global variable_global` por lo tanto elimina bastantes dolores de cabeza a la hora de depurar. – Miguelw Mar 17 '17 at 14:49
11

Una buena manera de entender el peligro y la pesadilla de las variables globales es por medio de examinar un ejemplo típico donde causa problemas. Voy a usar C# para los ejemplos, pero el principio aplica a calquier lenguaje de programación.

Tomemos el ejemplo siguiente:

public class MiClase
{
    // ...

    public void MiMetodo(string mivariable)
    {
        Console.WriteLine(mivariable.Length); // oh oh, mivariable tiene NULL, error!
    }

    // ...
}

Digamos que me han pedido de buscar la causa de una excepción lanzada arriba debido a que mivariable es null, aunque se supone que nunca debería serlo. En este caso, debido a que mivariable no es global, sino que es una variable con una entrada al método bien definido (un parámetro), con herramientas modernos como Visual Studio, es bien fácil de remontar el hilo de las llamadas al método para investigar en qué lugar mivariable se corrompió. El punto es que el ámbito de mivariable es limitado, de modo que no tengo que investigar un sinnúmero de lugares en mi código donde pudiera haber cambiado el valor de mivariable

En contraste, que tal si me tocara investigar el mismo error, pero con mivariable definida como una variable global:

public class MiClase
{
    public string mivariable; // global!!! Todos tienen acceso!

    // ...

    public void MiMetodo()
    {
        Console.WriteLine(mivariable.Length); // oh oh, mivariable tiene NULL, error!
    }

    // ...
}

Ahora se multiplica la complejidad de mi investigación, porque no solo tengo que ahora encontrar todos los lugares donde otras partes del código pudieran haber manipulado mivariable, y de seguro habrán muchas, sino que tengo que entender en qué orden todos estos otros lugares se ejecutan para saber en qué momento cambian los valores de mivariable. Esto es un tremendo dolor de cabeza.

De forma similar, no solo se complica la investigación de un problema cuando están envueltos variables globales, sino que, por las mismas razones, aumentan las probabilidades de introducir defectos sutiles al código por no entender bien el dónde y cuándo se usa la variable.

Y por supuesto, más complejo es el proyecto, y más código tienes, más se convierte en problema real. Este tipo de código es lo que se llega a conocer como código espaghetti por lo complicado que es entenderlo.

Y lo peor de todo, es que no hay justificación para el uso de variables globales. Usualmente, quién las introduce lo hace por pereza y por querer ahorrar algunas líneas de código. Pero el costo a largo plazo de hacer esto es muy grande.

sstan
  • 16,591
  • 2
  • 21
  • 45
10

Un maestro en la escuela nos decía que las variables globales son accesibles muy fácilmente, incluso por otra aplicación, esta situación aumenta considerablemente el riesgo de perdida o robo de información por agentes mal intencionados.

uomo_perfetto
  • 256
  • 1
  • 4
8

Una variable debe de tener el alcance mínimo necesario. No creo que haya ningún odio irracional a las variables globales. Eso sí, para tener una variable de alcance global debería estar justificado. No es un antipatrón utilizar una variable (o singleton) en su contexto adecuado. El problema viene cuando se amplía el contexto de la variable de forma innecesaria. Esto hace que partes del código puedan acceder a variables que no les incumben, bien sea por un fallo de diseño o por un intento de reutilización de un recurso, lo que puede llevarnos a un problema de ambigüedad en su uso muy peligroso.

Algo parecido ocurre con un deficiente encapsulamiento de nuestras clases ya que podemos dar visibilidad/acceso excesivo afectando incluso en una mayor dependencia que nos afectará en el futuro mantenimiento.

Alpha75
  • 324
  • 2
  • 7
6

Otro motivo aparte de los comentados:
Cuando se crea una función, es buena idea que no requiera nada externo a ella (como variables globales), para que se pueda usar por si sola pasando como parámetros cualquier valor que requiera.
De esta manera, la función es re-utilizable sin tener que recordar como está hecha (importante si la usan terceros) ni tener que añadir nada mas en tu código.

Arnau Castellví
  • 2,370
  • 8
  • 14
6

Resumiendo un poco la respuesta, el mal concepto de las variables globales es que son generadoras de bugs, en un proyecto pequeño no hay problema, en grandes proyectos y sobre todo modulares pueden volver loco a un "depurador" (persona que depure fallas).

Maurikius
  • 148
  • 1
  • 9
3

Otro detalle tener en cuenta en el uso de variables globales, es el riesgo de colisión con otras variables de otros programas. Cuantas más librerías, Plugins y widgets usamos mayor riesgo de colisión existe.

También tener en cuenta que las variables globales son una fuente de falta de fiabilidad y de inseguridad.

En algunos casos sé que la calidad del programa es mayor cuanto menos variables globales y funciones globales existan en nuestro código.

Diablo
  • 6,417
  • 2
  • 21
  • 40
1

Otro punto a considerar de las variables globales es el uso de memoria de la variable. Por ejemplo en un ciclo for.

  1. Si declaras el contador i dentro del for esta variable solo vivirá hasta que se termine de ejecutar el ciclo: for(int i;i>10;i++){}
  2. En cambio, si la declaras fuera del ciclo for, esta vivirá aún después de ejecutarse el ciclo: int i=0; for(i;i>10;i++){}

Por lo tanto tenemos que cuidar ese aspecto el uso de memoria en nuestras aplicaciones.

DoradoJ
  • 81
  • 5
1

Resumiendo después de todo lo explicado en general cuando se desarrolla software según la teoría de POO lo que se busca es un bajo nivel de acoplamiento y alta cohesión entre los componentes.

Ahora bien pues las variables globales generan alto nivel de acoplamiento, su control y manejo se vuelve confuso mientras mas crece el sistema y terminan siendo verdaderos dolores de cabeza.

Lo único que podrías definir de forma global seria una constante quisas, no meto las manos al fuego pero creo que es mejor a tener una variable libre que todo mundo tenga acceso a ella y encima la posibilidad de modificarla.

Saludos

Ricardo D. Quiroga
  • 2,478
  • 4
  • 25
  • 46
  • Yo me pregunto que ha pasado con los moderadores de Stackoverflow. Antes este tipo de debates, que parten de "preguntas" que causaban controversia eran cerradas, no porque sea malo debatir sobre algún tema, sino porque este espacio no es el apropiado. Muchas veces plantee preguntas similares de otros temas y siempre eran cerradas. Ya convertimos stackoverflow en un espacio de debate? – Camilo Bernal Apr 28 '17 at 12:01
  • 1
    @CamiloBernal si analizas este tipo de preguntas, te das cuenta al momento que solo fue generada por el autor para ganar puntos, tienes toda la razon que estas preguntas mas que generar respuestas correcta sirven como medio de debates. Saludos – Ricardo D. Quiroga Apr 28 '17 at 13:13
  • 1
    Lo irónico a tu duda, es que incluso los moderadores se suman al juego de responder este tipo de pregunta. Mira quienes respondieron jajaj, estamos jodidos. – Ricardo D. Quiroga Apr 28 '17 at 13:15