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.

Comentarios
  1. Programadores novatos llevándote la contraria en 3… 2… 1…

    Responder

    • Estaría encantado de poder “convertir” a alguno de ellos 😛

      Responder

  2. He pensado “las variables globales son malvadas” nada más ver el título. ¿En qué me convierte eso? 😀

    Responder

    • ¿En una persona decente que todo padre querría como marido para sus hijas?

      Responder

  3. Errepunto

    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…

    Responder

  4. programador_novato

    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 🙂

    Responder

    • q

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

      Responder

  5. q

    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 🙂 🙂

    Responder

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

    Responder

  7. MaG

    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

    Responder

  8. MaG

    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

    Responder

  9. raz

    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.

    Responder

  10. ElAlecs

    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.

    Responder

  11. [UT]_Roxx

    Al final parece que no ha contestado ningún novato. xD

    Responder

  12. ElAlecs

    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?

    Responder

    • Anónimo

      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.

      Responder

      • ElAlecs
      • ElAlecs

        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.

        Responder

        • Zanahoria

          Buena suerte intentando escribir una prueba unitaria para ese código, por ejemplo

          Responder

    • Anónimo

      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

      Responder

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

    Responder

  14. linkamp

    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.

    Responder

  15. buenos tips

    Responder

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

    Responder

    • ElAlecs

      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.

      Responder

  17. […] otro día leía en Mundo Geek cómo las variables globales son malvadas. En el post enumeraba las razones por las que las variables globales, lejos de ser nuestras amigas, […]

    Responder

  18. karlos

    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.

    Responder

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

      Responder

  19. karlos

    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

  20. gato

    como es que matan gatitos ?
    y gatotes ya no ?

    Responder

Deja un comentario