Variables globales

Una de las primeras cosas que se aprende al empezar a programar es que las variables globales son malvadas. Son casi tan despreciables como el infame goto, y (casi) nunca existen razones justificadas para utilizarlas. Martin Fowler lo expresa muy bien en su «Patterns of Enterprise Application Architecture» cuando dice que «cualquier variable global es siempre culpable hasta que se demuestre lo contrario». Pero, ¿por qué son tan odiadas las variables globales? ¿realmente son perjudiciales? ¿o se trata sólo de un odio irracional por parte de los puristas?

Las dos formas más comunes de proporcionar una dependencia a una función son, por un lado, los parámetros de la función:

function insertarItem($db, $item) {
	// Haz algo
	// ...
	
	$db->insertar($item);
	
	// Haz más algo
	// ...
}

y por otra parte, cualquier tipo de mecanismo de acceso global con el que contemos en el lenguaje (variables globales, patrón Singleton, etc). En este ejemplo en PHP utilizamos la palabra clave global para crear una variable local con una referencia a la variable global.

function insertarItem($item) {
	// Haz algo
	// ...
	
	global $db;
	$db->insertar($item);
	
	// Haz más algo
	// ...
}

En principio podría parecer que no hay mucha diferencia entre ambos ejemplos. Nada más lejos de la realidad.

El primer problema con el que nos encontramos es que las variables globales ocultan las dependencias, y transportan la información de un lado a otro por arte de magia. Imagina que quieres utilizar la función insertarItem dentro de un año. Podrías pensar que puedes usarla de esta forma:

insertarItem($item);

cuando, de hecho, la función puede estar esperando que hagas esto:

$GLOBALS['db'] = new Db('localhost', 'mibbdd', 'usuario', 'pass');
$_SESSION['permisos'] = new Usuario()-<permisos();
insertarItem($item);

¿Cómo podrías saberlo sin tener que leer todo el código?

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érico y de lo más difícil de depurar.

Las variables globales son malvadas por, al menos, 6 razones relacionadas:

  1. El código es más difícil de entender
  2. El código es más difícil de depurar
  3. El código es más difícil de testear
  4. El código es más difícil de mantener
  5. El código es más difícil de reutilizar
  6. Las variables globales matan gatitos

A pesar de todo puede tentarte usar variables globales para no tener que pasar el objeto $db a todas y cada una de las funciones que puedan necesitarlo, por ejemplo, y todas las funciones que llamen a funciones que puedan necesitarlo. En ese caso, en lugar de una colección de funciones con usos relacionados, puedes utilizar clases, y pasar la dependencia al objeto utilizando el constructor. Otras opciones son el uso de los patrones Abstract Factory, Service Locator o, mucho mejor, la inyección de dependencias (Dependency Injection, también llamado Inversion of Control).

Ojo, porque el patrón Singleton NO es una solución adecuada a este problema. Sí, es uno de los patrones explicados en Design patterns: elements of reusable object-oriented software, uno de los 10 libros míticos que todo programador debería leer, pero se suele abusar de él utilizándolo simplemente como una especie de variable global glorificada. Así las cosas, existen multitud de artículos que desaconsejan su utilización, e incluso Google desarrolló en su día un pequeño script para detectar el uso de singletons y poder eliminarlos.

32 comentarios en «Variables globales»

  1. Las variables globales en cualquier lenguaje hacen más mal que una plaga de langostas. ¡¡¿¿Par que se inventó la pila!!?? Para pasar argumentos, pardiez, usemos funciones con parámetros…

  2. Existen mil ejemplos donde el código generado por variables globales y gotos es mil veces mejor en cuanto a rendimiento que los espaguetis que se generan intentando evitarlos.

    La bajada en rendimiento a la hora de pasar parámetros a una función, especialmente en lenguajes orientados a pila, así como la escasez de registros en determinadas arquitecturas (vease x86 con sus 8 registros principales) hace que el uso de estas variables reduzca el número de accesos a memoria. Lo cual no quiere decir que se deba abusar de ellas.

    Existen casos, como en la programación de librerías sin reentrancia donde es casi obligatorio usar una variable global y así compartirla entre todas las llamadas a tener un memleak y una porción de heap perdida a cada llamada.

    Creo que desechar las variables globales, los gotos y el sin fin de sentencias que pueden hacer un código menos legible no es un buen argumento cuando se trata de programadores con una base. Totalmente de acuerdo de obligar a los programadores iniciados a no usar malas costumbres, pero de ahí a prohibirlas a todos me parece que se nos va la cabeza.

    Un saludo y lo siento si no os gusta mi opinión 🙂

    1. >La bajada en rendimiento a la hora de pasar parámetros a una
      >función, especialmente en lenguajes orientados a pila, así como la
      >escasez de registros en determinadas arquitecturas (vease x86 con
      >sus 8 registros principales) hace que el uso de estas variables
      >reduzca el número de accesos a memoria.

      No, el rendimiento no es una razón
      para usar globales ni para ahorrarse
      llamadas a procedimiento, excepto
      en casos muy concretos con requisitos
      muy especiales, que no son
      representativos.

      Eso no lo compro, y menos con las
      máquinas de hoy, en las que no
      se puede hacer ninguna suposición
      antes de medir el programa y ver
      por qué es lento.

  3. Novato && Globales == CeroAlCanto

    Por lo general, son malignas. Sobre todo
    porque te destrozan el cerebro a la hora de
    aprender a programar, a partir problemas
    y definir interfaces. Durante los primeros
    años, prohibidas.

    Dicho esto, tampoco son pecado si sabes lo
    que haces y las usas con moderación.
    El segmento de datos y el BSS también se
    inventaron por algo 🙂

    Otras veces son imprescindibles (memoria
    compartida, cierres, etc.).

    Como los goto*, son sólo para programadores con
    bastante experiencia que saben lo que hacen
    y las usan para cosas muy puntuales.

    * sí, los goto. A ver si ahora los que se pasan
    por el forro a Dijkstra y a Wirth con las excepciones
    de Java se van a escandalizar por esto 🙂 🙂

  4. Para empezar, sin entrar en si es bueno o no, y simplificando bastante: en algunos lenguajes, como por ejemplo C, una variable global implica que en cada llamada a una función, en su pila de ejecución (a efectos prácticos, en memoria) se deba incluir además de sus parámetros y sus variables locales, todas las variables globales, ya que también deben de ser visibles.

    Puede ser bueno e incluso recomendable si la inmensa mayoría de las funciones usan esas variables globales, e incluso en algunos casos se podría (como permite C) utilizar variables del tipo ‘register’ que indican a C que tienen un alto porcentaje de utilización y que conviene conservarlas en un registro del procesador y no tener que traerlas cada pocos ciclos.

    Sin embargo puede provocar un uso innecesario de memoria si no se usan más que en unas pocas partes del código. En este caso mi opinión es que hay que evitarlo a toda costa sobretodo si andamos escasos de memoria, como suele suceder en sistemas empotrados/embebidos.

    Ante todo, las características de los lenguajes no se diseñan porque sí, si están es porque son útiles en algunos casos. No está mal el uso de variables globales sino el uso «viciado» de variables globales.

  5. Estoy totalmente de acuerdo que utilizar variables global debería ser uno de los últimos recursos que un programador tenga que recurrir.

    Ahora bien esta no es razón de burla alguna o de algún sentimiento de superioridad, pues aunque tengan un poquito de razón, están totalmente equivocados.

    Utilizar variables globales puede llegar a ser una solución muy elegante y rápida; como ejemplo quiero decir algo que hice hace un tiempo «Utilicé variables globales y como regla para acceder a ella, programe toda una API completa como capa de control de acceso»

    GLOB_get_master_context(), GLOB_cleanall(), … GLOB_set_*()

    En cuanto a goto, dejaré que los Gurús les expliquen como utilizarla: Vease las lineas 295, 416, 689, 741, 797 del fichero /usr/src/linux/kernel/fork.c de su distribución favorita.

    Especialmente la función copy_process() linea 1294 (mismo fichero).

    Saludos. MaG

  6. Están también los threads.
    pthread_mutex_lock()
    pthread_mutex_unlock()

    ¿Cómo controlás varias decenas de threads de manera rápida y eficiente sin utilizar variables globales?
    -> difícil

  7. El único escenario donde me vería forzado a usar variables globales es el siguiente:

    3 de la mañana, con varias tazas de cafe encima y terminando de desarrollar un sistema para poder presentarlo a las 8 de la mañana a unos clientes importantes. Sería como a la !»#$%& las buenas practicas.

  8. Sigo sin entender el por que no se debe utilizar globales y singletones, si al final de cuentas en lenguajes como PHP al intentar sobre escribir clases te va la va a rayar antes de poder hacerlo, en caso de variables lo entiendo, pero en caso de funciones y clases no.

  9. Y entonces, ¿nadie debate el uso de singletones en PHP?

    Entiendo el punto de las variables globales, pero no de los singletones en clases/objetos. ¿O por qué está mal?

    1. La única diferencia entre usar
      global $db;
      y
      $db = DB::getInstance();
      es que el segundo puede parecer más orientado a objetos, pero tiene todos los defectos comentados, empezando porque la función, el método o la clase oculta sus dependencias.

      1. Si sigues el patrón de que toda clase está contenida en un archivo y usas el tipo de «namespaces» usado en la Pear puedes tener por ejemplo esto:

        db_DB::getInstance();

        y si a eso le agregas un gestor de autoload facilmente cargas las clases bajo demanda sin tener que ponerte a cargar cosas que no vas a usar.

        Y por otro lado sabes que DB esta dentro de un «paquete» que se llama db.

        No le veo el por que sea difícil de rastrear.

        A eso agregale una buena documentación PHPDoc, usa un buen IDE y puedes dar con cualquier cosa en cualquier momento.

    2. si inyectas la dependencia en vez de incrustarla a fuego puedes usar distintos comportamientos sin tener que modificar la clase y puedes pasar mockups de las dependencias para hacer pruebas fácilmente

  10. Totalmente de acuerdo el manejo de las variables globales debe de estar estrictamente justificado y tener presente bajo la documentación interna el por qué y para que se podría utilizar.

    Comparto tu opinión si no sabemos el para que se va a utilizar y tenemos infinidad de líneas de código podrían llegar a ser verdaderamente malditas

  11. En Zend Framework por ejemplo, se usa la classe Zend_Registry para registrar variables o objetos en el ámbito de la aplicación (para mi lo mismo que se busca conseguir con las variables globales) Usando este framework no veo justificado el uso de variables globales por los motivos expuestos en el articulo.

    El ejemplo seria algo así:

    function insertarItem($item) {
    $db = Zend_Registry::get(«db»);
    $db->insertar($item);
    }

    Previamente en el bootstrap deberíamos registrar la base de datos con

    Zend_Registry:set(«db», $db);

    En cuanto al patrón singleton lo veo útil en Java y no tanto en PHP ya que los objetos se crean y destruyen durante la request, en contraposición a los de Java que se quedan en memoria pendientes de que el GarbageCollector los limpie.

  12. Pues siento daros una mala noticia a todos:
    En Javascript, un lenguaje cada día más usado, no sólo puede usar variable globales, sino que es casi obligatorio usarlas cuando desarrollas cosas guapas con Ajax.
    Sí, son un dolor en general, pero me temo que están aquí para quedarse… a no ser que alguien cambien el javascript por algo mejor.. ojalá.

    Saludos.

    1. Pues se quizo cambiar a JS pero gracias a MS no se pudo, iba a ser un gran y mejor cambio muy al estilo ActionScript 3.

      Pero asi como dices, en JS todo se basa en los objetos globales y creación de objetos sin necesidad de usar clases.

  13. Pingback: What about… » Variables globales

  14. Pingback: [PHP] El patrón de diseño (o antipatrón) singleton vs Inyección de dependencias ¿Que debo usar? | Ajaxman

  15. lo que finalmente me ha convencido a sido lo de:
    «las variables globales matan gatitos» xD.

    En serio, cuando aprendi a programar, me enseñaron a parametrizar funciones/metodos, y casi inconsientemente lo hago y no se me habia pasado por la cabeza usar globales.. hasta ahora:

    Tengo que desarrollar un comando(en c) los parametros que se le pasan seran los mismos durante toda la ejecucion del mismo.. es entonces mas logico usar las globales(para guardar los params)?
    Me es ‘natural’ parametrizar las funciones y arrastrar, y de hecho asi le tengo hecho; pero veo que me ahorraria lo de ir arrastrando valores.

    consejos?

    Un saludo.

    1. ¿Puedes usar C++? Si las funciones tienen tanto en común suena a que serían candidatas perfectas para ser métodos de un objeto. Podrías pasarle los parámetros al constructor y compartirlos como miembros de la clase.

  16. No, es con C puro y duro, que usa syscalls
    (nose si c++ tb puede acceder a estas).

    En fin, he desarrollado mi programa y funciona a la perfeccion, pero he utilizado un par de variables globales(que almacenan los params pasados), y nose, me siento algo ‘auto-traicionado’ xD.

Responder a [UT]_Roxx Cancelar respuesta

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.