¿En qué se diferencian los globales de una base de datos?


250

Acabo de encontrarme con esta vieja pregunta preguntando qué hay de malo en el estado global, y la respuesta aceptada y más votada afirma que no puede confiar en ningún código que funcione con variables globales, porque algún otro código en algún otro lugar podría aparecer y modificar su valor y luego no sabes cuál será el comportamiento de tu código porque los datos son diferentes. Pero cuando miro eso, no puedo evitar pensar que es una explicación realmente débil, porque ¿en qué se diferencia eso de trabajar con datos almacenados en una base de datos?

Cuando su programa está trabajando con datos de una base de datos, no le importa si otro código en su sistema lo está cambiando, o incluso si un programa completamente diferente lo está cambiando, para el caso. No te importa cuáles son los datos; Ese es todo el punto. Lo único que importa es que su código maneja correctamente los datos que encuentra. (Obviamente, estoy pasando por alto el problema a menudo espinoso del almacenamiento en caché aquí, pero ignoremos eso por el momento).

Pero si los datos con los que está trabajando provienen de una fuente externa sobre la cual su código no tiene control, como una base de datos (o entrada del usuario, o un socket de red, o un archivo, etc.) y no hay nada de malo con eso, entonces, ¿cómo es que los datos globales dentro del código mismo, sobre los cuales su programa tiene un mayor grado de control, son algo malo cuando obviamente son mucho menos malos que cosas perfectamente normales que nadie ve como un problema?


117
Es agradable ver a los miembros veteranos desafían los dogmas un poco ...
svidgen

10
En una aplicación, generalmente proporciona un medio para acceder a la base de datos, este medio se pasa a las funciones que desean acceder a la base de datos. No haces eso con variables globales, simplemente sabes que están a la mano. Esa es una diferencia clave allí mismo.
Andy

45
El estado global es como tener una única base de datos con una sola tabla con una sola fila con infinitas columnas a las que se accede simultáneamente por un número arbitrario de aplicaciones.
BevynQ

42
Las bases de datos también son malas.
Stig Hemmer

27
Es entretenido "invertir" el argumento que haces aquí e ir en la otra dirección. Una estructura que tiene un puntero a otra estructura es lógicamente solo una clave foránea en una fila de una tabla que teclea a otra fila de otra tabla. ¿En qué se diferencia el trabajo con cualquier código, incluidas las listas vinculadas a pie, de la manipulación de datos en una base de datos? Respuesta: no lo es. Pregunta: ¿por qué entonces manipulamos las estructuras de datos en memoria y las estructuras de datos en base de datos usando herramientas tan diferentes? Respuesta: ¡Realmente no lo sé! Parece un accidente de la historia en lugar de un buen diseño.
Eric Lippert

Respuestas:


118

Primero, diría que la respuesta a la que se vincula exagera ese tema en particular y que el mal primario del estado global es que introduce el acoplamiento de formas impredecibles que pueden dificultar el cambio del comportamiento de su sistema en el futuro.

Pero profundizando en este tema aún más, hay diferencias entre el estado global en una aplicación orientada a objetos típica y el estado que se mantiene en una base de datos. Brevemente, los más importantes son:

  • Los sistemas orientados a objetos permiten reemplazar un objeto con una clase diferente de objeto, siempre que sea un subtipo del tipo original. Esto permite cambiar el comportamiento , no solo los datos .

  • El estado global en una aplicación generalmente no proporciona las fuertes garantías de coherencia que una base de datos: no hay transacciones durante las cuales se vea un estado coherente, ni actualizaciones atómicas, etc.

Además, podemos ver el estado de la base de datos como un mal necesario; Es imposible eliminarlo de nuestros sistemas. El estado global, sin embargo, es innecesario. Podemos eliminarlo por completo. Entonces, incluso si los problemas con una base de datos fueran igual de malos, aún podemos eliminar algunos de los problemas potenciales y una solución parcial es mejor que ninguna solución.


44
Creo que el punto de la coherencia es en realidad la razón principal: cuando se usan variables globales en el código, generalmente no se sabe cuándo se inicializan realmente. Las dependencias entre los módulos están profundamente ocultas dentro de la secuencia de llamadas, y cosas simples como intercambiar dos llamadas pueden producir errores realmente desagradables porque de repente alguna variable global ya no se inicializa correctamente cuando se usa por primera vez. Al menos ese es el problema que tengo con el código heredado con el que necesito trabajar, y que hace que la refactorización sea una pesadilla.
cmaster

24
@DavidHammen Realmente he trabajado en la simulación de estado mundial para un juego en línea, que está claramente en la categoría de aplicación de la que estás hablando, e incluso allí no usaría (y no) el estado global para ello. Incluso si se pueden lograr algunas mejoras de eficiencia mediante el uso del estado global, el problema es que el estado global no es escalable . Se vuelve difícil de usar una vez que pasa de una arquitectura de subproceso único a una arquitectura de subprocesos múltiples. Se vuelve ineficiente cuando te mueves a una arquitectura NUMA. Se vuelve imposible cuando te mueves a una arquitectura distribuida. El documento que cita data de ...
Julio

24
1993. Estos problemas eran menos problemáticos entonces. Los autores estaban trabajando en un sistema de procesador único, simulando interacciones de 1,000 objetos. En un sistema moderno, es probable que ejecute una simulación de ese tipo al menos en un sistema de doble núcleo, pero es muy probable que sea al menos 6 núcleos en un solo sistema. Para problemas más grandes aún, lo ejecutarías en un clúster. Para este tipo de cambio, debe evitar el estado global porque el estado global no se puede compartir de manera efectiva.
Julio

19
Creo que llamar al estado de la base de datos un "mal necesario" es un poco exagerado. Quiero decir, ¿desde cuándo el estado se convirtió en malvado? El estado es el propósito completo de una base de datos. Estado es información. Sin estado, todo lo que tienes son operadores. ¿De qué sirven los operadores sin algo para operar? Ese estado tiene que ir a alguna parte. Al final del día, la programación funcional es solo un medio para un fin y sin un estado que mute, no tendría sentido hacer nada en absoluto. Es un poco como un panadero llamando al pastel un mal necesario, no es malo. Es todo el punto de la cosa.
J ...

55
@DavidHammen "todavía hay algún objeto que sabe al menos un poco sobre cada objeto en el juego" No necesariamente es cierto. Una técnica importante en la simulación distribuida moderna es aprovechar la localidad y hacer aproximaciones para que los objetos distantes no necesiten saber todo lo que está lejos, solo qué datos les proporcionan los propietarios de esos objetos distantes.
JAB

75

Primero, ¿cuáles son los problemas con las variables globales, según la respuesta aceptada a la pregunta que ha vinculado?

Muy brevemente, hace que el estado del programa sea impredecible.

Las bases de datos son, la gran mayoría de las veces, compatibles con ACID. ACID aborda específicamente los problemas subyacentes que harían que un almacén de datos sea impredecible o poco confiable.

Además, el estado global perjudica la legibilidad de su código.

Esto se debe a que las variables globales existen en un ámbito muy alejado de su uso, tal vez incluso en un archivo diferente. Cuando utiliza una base de datos, está utilizando un conjunto de registros u objeto ORM que es local para el código que está leyendo (o debería ser).

Los controladores de bases de datos suelen proporcionar una interfaz coherente y comprensible para acceder a datos que son los mismos independientemente del dominio del problema. Cuando obtiene datos de una base de datos, su programa tiene una copia de los datos. Las actualizaciones son atómicas. Contraste con las variables globales, donde múltiples hilos o métodos pueden estar operando en el mismo dato sin atomicidad a menos que agregue la sincronización usted mismo. Las actualizaciones de los datos son impredecibles y difíciles de rastrear. Las actualizaciones pueden estar entrelazadas, lo que causa ejemplos de libros de texto estándar de pantanos de corrupción de datos multiproceso (por ejemplo, incrementos entrelazados).

Las bases de datos generalmente modelan datos diferentes que las variables globales para empezar, pero dejando eso de lado por un momento, las bases de datos están diseñadas desde cero para ser un almacén de datos compatible con ACID que mitigue muchas de las preocupaciones con las variables globales.


44
+1 Lo que estás diciendo es que las bases de datos tienen transacciones, lo que hace posible leer y escribir múltiples partes del estado global atómicamente. Buen punto, que solo puede evitarse mediante el uso de variables globales para cada pieza de información completamente independiente.
l0b0

1
Las transacciones @ l0b0 son el mecanismo que logra la mayoría de los objetivos de ACID, correcto. Pero la interfaz DB en sí misma aclara el código al llevar los datos a un ámbito más local. Piense en usar un conjunto de registros JDBC con un bloque de prueba con recursos, o una función ORM que obtiene un dato utilizando una sola llamada de función. Compare esto con la gestión de datos lejos del código que está leyendo en un lugar global.

1
Por lo tanto, estaría bien usar variables globales si copio el valor a una variable local (con un mutex) al comienzo de la función, modifico la variable local y luego copio el valor nuevamente a la variable global al final de ¿la función? (... preguntó retóricamente)
RM

1
@RM Mencionó dos puntos. Lo que arrojó podría abordar el primero (estado del programa impredecible), pero no aborda el segundo (la legibilidad de su código). De hecho, puede empeorar la legibilidad de su programa: P.
paseo

1
@RM Su función se ejecutará de manera consistente, sí. Pero entonces tendría la pregunta de si algo más había modificado la variable global mientras tanto, y esa modificación era más importante de lo que le está escribiendo. Las bases de datos también pueden tener el mismo problema, por supuesto.
Graham

45

Ofrecería algunas observaciones:

Sí, una base de datos es un estado global.

De hecho, es un estado súper global, como usted señaló. Es universal! Su alcance implica cualquier cosa o persona que se conecte a la base de datos. Y sospecho que muchas personas con años de experiencia pueden contarte historias de horror sobre cómo las "cosas extrañas" en los datos llevaron a un "comportamiento inesperado" en una o más de las aplicaciones relevantes ...

Una de las posibles consecuencias del uso de una variable global es que dos "módulos" distintos usarán esa variable para sus propios fines distintos. Y hasta ese punto, una tabla de base de datos no es diferente. Puede ser víctima del mismo problema.

Hmm ... Aquí está la cosa:

Si un módulo no funciona extrínsecamente de alguna manera, no hace nada.

Un módulo útil puede recibir datos o puede encontrarlo . Y, puede devolver datos o puede modificar el estado. Pero, si no interactúa con el mundo externo de alguna manera, es mejor que no haga nada.

Ahora, nuestra preferencia es recibir datos y devolver datos. La mayoría de los módulos son simplemente más fáciles de escribir si se pueden escribir sin tener en cuenta lo que está haciendo el mundo exterior. Pero en última instancia, algo necesita encontrar los datos y modificar ese estado externo y global.

Además, en aplicaciones del mundo real, los datos existen para que puedan ser leídos y actualizados por varias operaciones. Algunos problemas se evitan mediante bloqueos y transacciones. Pero, en principio , evitar que estas operaciones entren en conflicto entre sí , al final del día, simplemente implica pensar cuidadosamente. (Y cometer errores ...)

Pero también, generalmente no estamos trabajando directamente con el estado global.

A menos que la aplicación viva en la capa de datos (en SQL o lo que sea), los objetos con los que trabajan nuestros módulos son en realidad copias del estado global compartido. Podemos hacer lo que queramos sin impacto en el estado real compartido.

Y, en los casos en que necesitamos mutar ese estado global, bajo el supuesto de que los datos que nos dieron no han cambiado, generalmente podemos realizar el mismo tipo de bloqueo que haríamos en nuestros globales.

Y finalmente, generalmente hacemos cosas diferentes con las bases de datos de lo que podríamos hacer con los globales traviesos.

Un mundo travieso y roto se ve así:

Int32 counter = 0;

public someMethod() {
  for (counter = 0; counter < whatever; counter++) {
    // do other stuff.
  }
}

public otherMethod() {
  for (counter = 100; counter < whatever; counter--) {
    // do other stuff.
  }
}

Simplemente no usamos bases de datos para cosas en proceso / operativas como esas. Y podría ser la naturaleza lenta de la base de datos y la relativa conveniencia de una variable simple lo que nos disuade: nuestra interacción lenta e incómoda con las bases de datos simplemente los convierte en malos candidatos para muchos de los errores que históricamente hemos cometido con las variables.


3
La forma de garantizar (ya que no podemos suponer) "que los datos que nos dieron no han cambiado" en una base de datos sería una transacción.
l0b0

Sí ... se suponía que eso estaba implícito con "un bloqueo igual"
svidgen

Pero, puede ser difícil pensar cuidadosamente al final del día.

Sí, las bases de datos son de hecho un estado global, razón por la cual es tan tentador compartir datos usando algo como git o ipfs.
William Payne

21

No estoy de acuerdo con la afirmación fundamental de que:

Cuando su programa está trabajando con datos de una base de datos, no le importa si otro código en su sistema lo está cambiando, o incluso si un programa completamente diferente lo está cambiando, para el caso.

Mi pensamiento inicial fue "Wow. Just Wow". Se gasta mucho tiempo y esfuerzo tratando de evitar exactamente esto, y resolviendo qué compensaciones y compromisos funcionan para cada aplicación. Simplemente ignorarlo es una receta para el desastre.

Pero también estoy de acuerdo en un nivel arquitectónico. Una variable global no es solo un estado global. Es un estado global al que se puede acceder desde cualquier lugar de forma transparente. A diferencia del uso de una base de datos, debe tener un identificador (a menos que almacene el identificador en una variable global ...)

Por ejemplo, usar una variable global podría verse así

int looks_ok_but_isnt() {
  return global_int++;
}

int somewhere_else() {
  ...
  int v = looks_ok_but_isnt();
  ...
}

Pero hacer lo mismo con una base de datos debería ser más explícito sobre lo que está haciendo

int looks_like_its_using_a_database( MyDB * db ) {
   return db->get_and_increment("v");
}

int somewhere_else( MyBD * db ) { 
   ...
   v = looks_like_its_using_a_database(db);
   ...
}

La base de datos obviamente está jugando con una base de datos. Si no desea usar una base de datos, puede usar el estado explícito y se ve casi igual que el caso de la base de datos.

int looks_like_it_uses_explicit_state( MyState * state ) {
   return state->v++;
}


int somewhere_else( MyState * state ) { 
   ...
   v = looks_like_it_uses_explicit_state(state);
   ...
}

Entonces argumentaría que usar una base de datos es mucho más como usar un estado explícito que usar variables globales.


2
Sí, pensé que era interesante cuando el OP dijo: " No te importa cuáles son los datos; ese es el punto ", si no nos importa, ¿por qué almacenarlos? He aquí una idea: vamos a dejar de usar variables y datos en absoluto . Eso debería hacer las cosas mucho más simples. "¡Detengan al mundo, quiero bajar!"

1
+1 Los diferentes subprocesos o aplicaciones que escriben y leen desde la misma base de datos es una fuente potencial de una gran cantidad de problemas bien conocidos, por lo que siempre debe haber una estrategia para tratar esto, ya sea a nivel de la base de datos o de la aplicación, o ambos. Por lo tanto, definitivamente NO es cierto que a usted (el desarrollador de la aplicación) no le importe quién más está leyendo o escribiendo desde la base de datos.
Andres F.

1
+1 En una nota al margen, esta respuesta explica más o menos lo que más odio de la inyección de dependencia. Oculta este tipo de dependencias.
jpmc26

@ jpmc26 Podría estar marcando palabras, pero ¿no es lo anterior un buen ejemplo de cómo la inyección de dependencia (en oposición a la búsqueda global) ayuda a hacer explícitas las dependencias? Me parece que prefieres tener problemas con ciertas API, como quizás la magia de anotación utilizada por JAX-RS y Spring.
Emil Lundberg

2
@EmilLundberg No, el problema es cuando tienes una jerarquía. La inyección de dependencia oculta las dependencias de los niveles inferiores del código en los niveles superiores, lo que dificulta el seguimiento de qué cosas interactúan. Por ejemplo, si MakeNewThingdepende MakeNewThingInDby mi clase de controlador usa MakeNewThing, entonces no está claro en el código de mi controlador que estoy modificando la base de datos. Entonces, ¿qué pasa si uso otra clase que realmente confirma mi transacción actual a la base de datos? DI hace que sea muy difícil controlar el alcance de un objeto.
jpmc26

18

El punto de que la única razón por la que no se puede confiar en las variables globales ya que el estado se puede cambiar en otro lugar es, en sí mismo, no una razón suficiente para no usarlas, de acuerdo (¡es una razón bastante buena!). Es probable que la respuesta describiera principalmente el uso en el que restringir el acceso de una variable a solo áreas de código que le preocupan tendría más sentido.

Sin embargo, las bases de datos son un asunto diferente, porque están diseñadas con el propósito de ser accedidas "globalmente", por así decirlo.

Por ejemplo:

  • Las bases de datos generalmente tienen una validación de tipo y estructura incorporada que va más allá del idioma que accede a ellas.
  • Las bases de datos se actualizan casi por unanimidad en función de las transacciones, lo que evita estados inconsistentes, donde no hay garantías de cómo se verá el estado final en un objeto global (a menos que esté oculto detrás de un singleton)
  • La estructura de la base de datos se documenta al menos implícitamente en función de la estructura de tabla u objeto, más que la aplicación que la utiliza.

Sin embargo, lo más importante es que las bases de datos tienen un propósito diferente que una variable global. Las bases de datos son para almacenar y buscar grandes cantidades de datos organizados, donde las variables globales sirven a nichos específicos (cuando es justificable).


1
Huh Me ganaste mientras estaba a mitad de camino de escribir una respuesta casi idéntica. :)
Julio

@Jules su respuesta proporciona detalles más desde el lado de la aplicación de las cosas; quédatelo.
Jeffrey Sweeney

Pero, a menos que dependa completamente de los procedimientos almacenados para el acceso a los datos, toda esa estructura aún no logrará exigir que las tablas se usen según lo previsto. O que las operaciones se realizan en el orden apropiado. O que los bloqueos (transacciones) se crean según sea necesario.
svidgen

Hola, ¿siguen siendo aplicables los puntos 1 y 3 si está utilizando un lenguaje de tipo estático como Java?
Jesvin Jose

@aitchnyu No necesariamente. El punto que se destaca es que las bases de datos se crean con el propósito de compartir datos de manera confiable, donde las variables globales generalmente no lo son. Un objeto que implementa una interfaz de autodocumentación en un lenguaje estricto tiene un propósito diferente que incluso una base de datos NoSQL de tipo suelto.
Jeffrey Sweeney

10

Pero cuando miro eso, no puedo evitar pensar que es una explicación realmente débil, porque ¿en qué se diferencia eso de trabajar con datos almacenados en una base de datos?

O diferente de trabajar con un dispositivo interactivo, con un archivo, con memoria compartida, etc. Un programa que hace exactamente lo mismo cada vez que se ejecuta es un programa muy aburrido y bastante inútil. Entonces sí, es un argumento débil.

Para mí, la diferencia que marca la diferencia con respecto a las variables globales es que forman líneas de comunicación ocultas y desprotegidas. Leer desde un teclado es muy obvio y está protegido. Tengo que realizar una determinada llamada de función y no puedo acceder al controlador del teclado. Lo mismo se aplica al acceso a archivos, la memoria compartida y, por ejemplo, las bases de datos. Es obvio para el lector del código que esta función lee desde el teclado, que la función accede a un archivo, alguna otra función accede a la memoria compartida (y es mejor que haya protecciones al respecto), y aún otra función accede a una base de datos.

Con las variables globales, por otro lado, no es obvio en absoluto. La API dice que llame foo(this_argument, that_argument). No hay nada en la secuencia de llamada que diga que la variable global g_DangerWillRobinsondebe establecerse en algún valor, pero antes de llamar foo(o examinarla después de llamar foo).


Google prohibió el uso de argumentos de referencia no constantes en C ++ principalmente porque no es obvio para el lector del código que foo(x)cambiará xporque eso footoma una referencia no constante como argumento. (Compare con C #, que dicta que tanto la definición de la función como el sitio de la llamada deben calificar un parámetro de referencia con la refpalabra clave). Si bien no estoy de acuerdo con el estándar de Google sobre esto, entiendo su punto.

El código se escribe una vez y se modifica varias veces, pero si es bueno, se lee muchas, muchas veces. Las líneas de comunicación ocultas son muy mal karma. La referencia no constante de C ++ representa una línea de comunicación oculta menor. Una buena API o un buen IDE me mostrará que "¡Oh! Esta es una llamada por referencia". Las variables globales son una gran línea de comunicación oculta.


Tu respuesta tiene más sentido.
Billal Begueradj

8

Creo que la explicación citada simplifica demasiado el tema hasta el punto en que el razonamiento se vuelve ridículo. Por supuesto, el estado de una base de datos externa contribuye al estado global. La pregunta importante es cómosu programa depende del estado global (mutable). Si una función de biblioteca para dividir cadenas en espacios en blanco dependiera de los resultados intermedios almacenados en una base de datos, me opondría a este diseño al menos tanto como me opondría a una matriz de caracteres global utilizada para el mismo propósito. Por otro lado, si decide que su aplicación no necesita un DBMS completo para almacenar datos comerciales en este momento y una estructura global de valor-clave en memoria funcionará, esto no es necesariamente un signo de diseño deficiente. Lo importante es que, independientemente de la solución que elija para almacenar sus datos, esta opción está aislada en una porción muy pequeña del sistema, por lo que la mayoría de los componentes pueden ser independientes de la solución elegida para el despliegue y la prueba de la unidad aislada y desplegada. La solución se puede cambiar en un momento posterior con poco esfuerzo.


8

Como ingeniero de software que trabaja predominantemente con firmware incorporado, casi siempre uso variables globales para cualquier cosa que vaya entre módulos. De hecho, es la mejor práctica para embebido. Se asignan de forma estática, por lo que no existe el riesgo de volar el montón / pila y no se necesita tiempo adicional para la asignación / limpieza de la pila en la entrada / salida de la función.

La desventaja de esto es que hay que tener en cuenta cómo se utilizan estas variables, y mucho de eso se reduce a la misma clase de pensamiento que entra en la base de datos-disputas. Cualquier lectura / escritura asíncrona de variables DEBE ser atómica. Si más de un lugar puede escribir una variable, se debe pensar en asegurarse de que siempre escriben datos válidos, de modo que la escritura anterior no se reemplace arbitrariamente (o ese reemplazo arbitrario es algo seguro). Si la misma variable se lee más de una vez, se debe pensar en lo que sucede si la variable cambia de valor entre lecturas, o se debe tomar una copia de la variable al inicio para que el procesamiento se realice utilizando un valor consistente, incluso si ese valor se vuelve obsoleto durante el procesamiento.

(Para el último, en mi primer día de un contrato trabajando en un sistema de contramedidas de aeronaves, tan altamente relacionado con la seguridad, el equipo de software estaba mirando un informe de error que habían estado tratando de resolver durante una semana más o menos. Tuve el tiempo justo para descargar las herramientas de desarrollo y una copia del código. Pregunté "¿no podría actualizarse esa variable entre lecturas y causarla?", Pero realmente no obtuve una respuesta. ¿Alguien nuevo sabe, después de todo? Entonces, mientras todavía lo discutían, agregué un código de protección para leer la variable atómicamente, hice una compilación local y básicamente dije "Hola chicos, intenten esto". Manera de demostrar que valía mi tasa de contratación . :)

Por lo tanto, las variables globales no son una cosa inequívocamente mala, pero lo dejan abierto a una amplia gama de problemas si no piensa en ellas con cuidado.


7

Dependiendo de qué aspecto esté juzgando, las variables globales y el acceso a la base de datos pueden estar muy separados, pero siempre que los juzguemos como dependencias, son lo mismo.

Consideremos que la definición de programación funcional de una función pura establece que debe depender únicamente de los parámetros que toma como entradas, produciendo una salida determinista. Es decir, dado el mismo conjunto de argumentos dos veces, debe producir el mismo resultado.

Cuando una función depende de una variable global, ya no se puede considerar pura, ya que, para el mismo conjunto o argumentos, puede producir resultados diferentes porque el valor de la variable global puede haber cambiado entre las llamadas.

Sin embargo, la función todavía puede verse como determinista si consideramos que la variable global es tan parte de la interfaz de la función como sus otros argumentos, por lo que no es el problema. El problema es solo que esto está oculto hasta el momento en que nos sorprende el comportamiento inesperado de funciones aparentemente obvias, luego lee sus implementaciones para descubrir las dependencias ocultas .

En esta parte, el momento en que una variable global se convierte en una dependencia oculta es lo que los programadores consideramos malvado. Hace que el código sea más difícil de razonar, difícil de predecir cómo se comportará, difícil de reutilizar, difícil de probar y, especialmente, aumenta el tiempo de depuración y reparación cuando se produce un problema.

Lo mismo sucede cuando ocultamos la dependencia de la base de datos. Podemos tener funciones u objetos que realicen llamadas directas a consultas y comandos de la base de datos, ocultando estas dependencias y causándonos exactamente el mismo problema que causan las variables globales; o podemos hacerlos explícitos, lo que, como resulta, se considera una práctica recomendada que tiene muchos nombres, como patrón de repositorio, almacén de datos, puerta de enlace, etc.

PD: Hay otros aspectos que son importantes para esta comparación, como si la concurrencia está involucrada, pero ese punto está cubierto por otras respuestas aquí.


Me gusta que hayas tomado esto desde el ángulo de las dependencias.
cbojar

6

Bien, comencemos desde el punto histórico.

Estamos en una aplicación antigua, escrita en su combinación típica de ensamblaje y C. No hay funciones, solo procedimientos . Cuando desea pasar un argumento o valor de retorno de un procedimiento, utiliza una variable global. No es necesario decir que es bastante difícil realizar un seguimiento y, en general, cada procedimiento puede hacer lo que quiera con cada variable global. Como era de esperar, las personas recurrieron a pasar argumentos y devolver valores de una manera diferente tan pronto como fue posible (a menos que fuera crítico para el rendimiento no hacerlo, por ejemplo, mire el código fuente de Build Engine (Duke 3D)). El odio a las variables globales nació aquí: tenía muy poca idea de qué parte del estado global leería y cambiaría cada procedimiento, y realmente no podía anidar las llamadas a procedimientos de forma segura.

¿Significa esto que el odio global variable es cosa del pasado? No exactamente.

Primero, debo mencionar que he visto exactamente el mismo enfoque para pasar argumentos en el proyecto en el que estoy trabajando en este momento. Para pasar dos instancias de tipo de referencia en C #, en un proyecto que tiene aproximadamente 10 años. Literalmente, no hay una buena razón para hacerlo de esta manera, y es muy probable que haya surgido de un desempaque de carga o de un completo malentendido sobre cómo funciona C #.

El punto más importante es que al agregar variables globales, está expandiendo el alcance de cada pieza de código que tiene acceso a esa variable global. ¿Recuerdas todas esas recomendaciones como "mantén tus métodos cortos"? Si tiene 600 variables globales (de nuevo, ejemplo del mundo real: /), todos los alcances de sus métodos se expanden implícitamente por esas 600 variables globales, y no hay una manera simple de realizar un seguimiento de quién tiene acceso a qué.

Si se hace mal (la forma habitual :)), las variables globales pueden tener acoplamiento entre sí. Pero no tiene idea de cómo se acoplan, y no existe un mecanismo para garantizar que el estado global siempre sea coherente. Incluso si introduce secciones críticas para tratar de mantener las cosas consistentes, encontrará que se compara mal con una base de datos ACID adecuada:

  • No hay forma de deshacer una actualización parcial, a menos que conserve los valores anteriores antes de la "transacción". No es necesario decir, en este punto, pasar un valor como argumento ya es una victoria :)
  • Todos los que accedan al mismo estado deben cumplir con el mismo proceso de sincronización. Pero no hay forma de hacer cumplir esto: si olvida configurar la sección crítica, está jodido.
  • Incluso si sincroniza correctamente todos los accesos, puede haber llamadas anidadas que acceden al estado parcialmente modificado. Esto significa que puede llegar a un punto muerto (si sus secciones críticas no son remanentes) o tratar con datos inconsistentes (si son remanentes).

¿Es posible resolver estos problemas? Realmente no. Necesita encapsulación para manejar esto, o una disciplina realmente estricta. Es difícil hacer las cosas bien, y eso generalmente no es una muy buena receta para el éxito en el desarrollo de software :)

Un alcance más pequeño tiende a hacer que el código sea más fácil de razonar. Las variables globales hacen que incluso las piezas de código más simples incluyan grandes extensiones de alcance.

Por supuesto, esto no significa que el alcance global sea malo. Simplemente no debería ser la primera solución que elija, es un ejemplo típico de "simple de implementar, difícil de mantener".


Se parece mucho al mundo físico: es muy difícil hacer retroceder las cosas.

Esta es una buena respuesta, pero podría soportar una declaración de tesis (TL; sección DR) desde el principio.
jpmc26

6

Una variable global es una herramienta, puede usarse para bien y para mal.

Una base de datos es una herramienta, puede usarse para bien y para mal.

Como señala el póster original, la diferencia no es tan grande.

Los estudiantes sin experiencia a menudo piensan que los errores son algo que les sucede a otras personas. Los maestros usan "Las variables globales son malas" como una razón simplificada para penalizar el mal diseño. Los estudiantes generalmente no entienden que el hecho de que su programa de 100 líneas esté libre de errores no significa que se puedan usar los mismos métodos para los programas de 10000 líneas.

Cuando trabaja con bases de datos, no puede simplemente prohibir el estado global, ya que de eso se trata el programa. En su lugar, obtienes más pautas de detalles como ACID y Normal Forms, etc.

Si las personas usaran el enfoque ACID para las variables globales, no serían tan malas.

Por otro lado, si diseñas mal las bases de datos, pueden ser pesadillas.


3
Reclamo típico de los estudiantes en stackoverflow: ¡Ayúdame! ¡Mi código es perfecto, pero no funciona bien!
David Hammen

"Enfoque ACID para las variables globales" - ver referencias en Clojure.
Charles Duffy

@DavidHammen y ¿crees que los profesionales tienen un cerebro diferente a los estudiantes?
Billal Begueradj

@BillalBEGUERADJ - Esa es la diferencia entre profesionales y estudiantes. Sabemos que a pesar de años de experiencia y a pesar de los mejores esfuerzos de revisión de códigos, pruebas, etc., nuestro código no es perfecto.
David Hammen


5

Para mí, el mal principal es que los Globals no tienen protección contra los problemas de concurrencia. Puede agregar mecanismos para manejar tales problemas con Globals, pero encontrará que cuantos más problemas de concurrencia resuelva, más Globals comenzará a imitar una base de datos. El mal secundario no es un contrato de uso.


3
Por ejemplo, errnoen C.
David Hammen

1
Esto explica exactamente por qué los globales y las bases de datos no son lo mismo. Puede haber otras diferencias, pero su publicación específica destruye el concepto por completo. Si dio un ejemplo de código rápido, estoy seguro de que obtendría muchos votos a favor. por ejemplo, MyFunc () {x = globalVar * 5; // .... Algún otro procesamiento; y = globalVar * 34; // Vaya, algún otro subproceso podría haber cambiado globalVar durante algún otro procesamiento y x e y están utilizando diferentes valores para globalVar en sus cálculos, lo que casi seguro no daría resultados deseables.
Dunk

5

Algunas de las otras respuestas intentan explicar por qué es bueno usar una base de datos. ¡Están equivocados! Una base de datos es un estado global y, como tal, es tan malvada como un singleton o una variable global. ¡Es todo un error usar una base de datos cuando puedes usar fácilmente un Mapa local o una Matriz en su lugar!

Las variables globales permiten el acceso global, lo que conlleva el riesgo de abuso. Las variables globales también tienen ventajas. Generalmente se dice que las variables globales son algo que debes evitar, no algo que nunca deberías usar. Si puede evitarlos fácilmente, debe evitarlos. Pero si los beneficios superan los inconvenientes, ¡por supuesto, debería usarlos! *

Exactamente lo mismo ** se aplica a las bases de datos, que son de estado global, al igual que las variables globales. Si puede hacerlo sin acceder a una base de datos, y la lógica resultante hace todo lo que necesita y es igualmente complejo, el uso de una base de datos agrega un mayor riesgo a su proyecto, sin ningún beneficio correspondiente.

En la vida real, muchas aplicaciones requieren un estado global por diseño, a veces incluso un estado global persistente; por eso tenemos archivos, bases de datos, etc.


* La excepción aquí son los estudiantes. Tiene sentido prohibir a los estudiantes el uso de variables globales para que tengan que aprender cuáles son las alternativas.

** Algunas respuestas afirman incorrectamente que las bases de datos están de alguna manera mejor protegidas que otras formas de estado global (la pregunta es explícitamente sobre el estado global , no solo las variables globales). Eso es una mierda. La protección principal que se ofrece en el escenario de la base de datos es por convención, que es exactamente la misma para cualquier otro estado global. La mayoría de los idiomas también permiten una gran protección adicional para el estado global, en forma de constclases que simplemente no permiten cambiar su estado después de que se haya establecido en el constructor, o captadores y establecedores que pueden tener en cuenta la información de subprocesos o el estado del programa.


2

En cierto sentido, la distinción entre variables globales y una base de datos es similar a la distinción entre miembros privados y públicos de un objeto (suponiendo que alguien todavía use campos públicos). Si piensa en todo el programa como un objeto, las variables globales son las variables privadas y la base de datos son los campos públicos.

La distinción clave aquí es de responsabilidad asumida.

Cuando escribe un objeto, se supone que cualquiera que mantenga los métodos de miembro se asegurará de que los campos privados se mantengan bien comportados. Pero ya renuncia a cualquier suposición sobre el estado de los campos públicos y los trata con mucho cuidado.

El mismo supuesto se aplica en un nivel más amplio a la base de datos global v / s. Además, el lenguaje de programación / ecosistema garantiza restricciones de acceso en privado v / s público en el mismo estado en que se aplica en la base de datos global (memoria no compartida) v / s.

Cuando entra en juego el subprocesamiento múltiple, el concepto de base de datos privada v / s pública v / s global v / s es simplemente distinciones a lo largo de un espectro.

static int global; // within process memory space
static int dbvar; // mirrors/caches data outside process memory space

class Cls {
    public: static int class_public; // essentially the same as global
    private: static int class_private; // but public to all methods in class

    private: static void method() {
        static int method_private; // but public to all scopes in method
        // ...
        {
            static int scope1_private; // mutex guarded
            int the_only_truly_private_data;
        }
        // ...
        {
            static int scope2_private; // mutex guarded
        }
    }
}

1

Una base de datos puede ser un estado global, pero no tiene que serlo todo el tiempo. No estoy de acuerdo con la suposición de que no tienes control. Una forma de gestionar eso es el bloqueo y la seguridad. Esto se puede hacer en el registro, la tabla o la base de datos completa. Otro enfoque es tener algún tipo de campo de versión que evite el cambio de un registro si los datos están obsoletos.

Al igual que una variable global, los valores en una base de datos se pueden cambiar una vez que se desbloquean, pero hay muchas formas de controlar el acceso (no le dé a todos los desarrolladores la contraseña de la cuenta que tiene permiso para cambiar los datos). Si tiene una variable que tiene acceso limitado, no es muy global.


0

Hay varias diferencias:

  • El valor de una base de datos se puede modificar sobre la marcha. El valor de un global que se establece en el código, por otro lado, no se puede cambiar a menos que vuelva a implementar su aplicación y modifique su código. De hecho, esto es intencional. Una base de datos es para valores que pueden cambiar con el tiempo, pero las variables globales solo deben ser para cosas que nunca cambiarán y cuando no contienen datos reales.

  • Un valor de base de datos (fila, columna) tiene un contexto y un mapeo relacional en la base de datos. Esta relación se puede extraer y analizar fácilmente utilizando herramientas como Jailer (por ejemplo). Una variable global, por otro lado, es ligeramente diferente. Puede encontrar todos los usos, pero le sería imposible decirme todas las formas en que la variable interactúa con el resto de su mundo.

  • Las variables globales son más rápidas . Obtener algo de una base de datos requiere que se realice una conexión de base de datos, una selección para ejecutar y luego la conexión de la base de datos debe cerrarse. Cualquier tipo de conversiones que pueda necesitar se suman a eso. Compare eso con un acceso global en su código.

Estos son los únicos que puedo pensar en este momento, pero estoy seguro de que hay más. En pocas palabras, son dos cosas diferentes y deben usarse para diferentes objetivos .


0

Por supuesto, los globales no siempre son inapropiados. Existen porque tienen un uso legítimo. El principal problema con los globales, y la fuente principal de la advertencia para evitarlos, es que el código que usa un global está adjunto a ese único y global.

Por ejemplo, considere un servidor HTTP que almacena el nombre del servidor.

Si almacena el nombre del servidor en un archivo global, el proceso no puede ejecutar simultáneamente la lógica de dos nombres de servidor diferentes. Quizás el diseño original nunca contempló ejecutar más de una instancia de servidor a la vez, pero si luego decide que quiere hacerlo, simplemente no puede hacerlo si el nombre del servidor está en un global.

Por el contrario, si el nombre del servidor está en una base de datos, no hay problema. Simplemente puede crear una instancia de esa base de datos para cada instancia del servidor HTTP. Como cada instancia del servidor tiene su propia instancia de la base de datos, puede tener su propio nombre de servidor.

Entonces, la principal objeción a los globales, puede haber un solo valor para todo el código que accede a ese global, no se aplica a las entradas de la base de datos. El mismo código puede acceder fácilmente a instancias de bases de datos distintas que tienen valores diferentes para una entrada en particular.


0

Creo que esta es una pregunta interesante, pero es un poco difícil de responder porque hay dos cuestiones principales que se combinan bajo el término "estado global". El primero es el concepto de "acoplamiento global". La prueba de eso es que la alternativa dada para el estado global es la inyección de dependencia. La cuestión es que la DI no necesariamente elimina el estado global. Es decir, es absolutamente posible y común inyectar dependencias en el estado global. Lo que DI hace es eliminar el acoplamiento que viene con las variables globales y el patrón Singleton comúnmente utilizado. Aparte de un diseño un poco menos obvio, hay muy pocos inconvenientes para eliminar este tipo de acoplamiento y los beneficios de eliminar el acoplamiento aumenta exponencialmente con el número de dependencias de esos globales.

El otro aspecto de esto es el estado compartido. No estoy seguro de si existe una distinción realmente clara entre el estado globalmente compartido y el estado compartido en general, pero los costos y beneficios son mucho más matizados. En pocas palabras, hay innumerables sistemas de software que requieren un estado compartido para ser útiles. Bitcoin, por ejemplo, es una forma muy inteligente de compartir el estado globalmente (literalmente) de manera descentralizada. Compartir el estado mutable correctamente sin crear grandes cuellos de botella es difícil pero útil. Entonces, si realmente no necesita hacerlo, puede simplificar su aplicación minimizando el estado mutable compartido.

Entonces, la cuestión de cómo las bases de datos difieren de las globales también se bifurca en estos dos aspectos. ¿Introducen el acoplamiento? Sí, pueden, pero depende mucho de cómo está diseñada la aplicación y cómo está diseñada la base de datos. Hay demasiados factores para tener una respuesta única sobre si las bases de datos introducen el acoplamiento global sin detalles del diseño. En cuanto a si introducen el intercambio de estado, bueno, ese es el punto principal de una base de datos. La pregunta es si lo hacen bien. Nuevamente, creo que esto es demasiado complicado de responder sin mucha otra información, como las alternativas y muchas otras compensaciones.


0

Lo pensaría de manera ligeramente diferente: el comportamiento de "variable global" es un precio pagado por los administradores de bases de datos (DBA) porque es un mal necesario para hacer su trabajo.

El problema con las variables globales, como han señalado varios otros, no es arbitrario. El problema es que su uso hace que el comportamiento de su programa sea cada vez menos predecible porque se hace más difícil determinar quién está usando la variable y de qué manera. Este es un gran problema para el software moderno, porque normalmente se le pide al software moderno que haga muchas cosas flexibles. Puede hacer miles de millones o incluso billones de manipulaciones complejas del estado durante el transcurso de una carrera. La capacidad de demostrar declaraciones verdaderas sobre lo que hará ese software en esos miles de millones o trillones de operaciones es extremadamente valiosa.

En el caso del software moderno, todos nuestros idiomas proporcionan herramientas para ayudar en esto, como la encapsulación. La elección de no usarlo es innecesaria, lo que lleva a la mentalidad de "los globales son malvados". En muchas regiones del campo de desarrollo de software, las únicas personas que los usan son personas que no saben codificar mejor. Esto significa que no solo son problemas directamente, sino que indirectamente sugieren que el desarrollador no sabía lo que estaban haciendo. En otras regiones, encontrará que los globales son totalmente normales (el software incorporado, en particular, ama los globales, en parte porque funcionan bien con los ISR). Sin embargo, en medio de los muchos desarrolladores de software que existen, son la voz minoritaria, por lo que la única voz que escuchas son "los globales son malvados".

El desarrollo de bases de datos es una de esas situaciones de voz minoritarias. Las herramientas necesarias para hacer el trabajo DBA son muy poderosas, y su teoría no se basa en la encapsulación. Para buscar cada instante de rendimiento de sus bases de datos, necesitan un acceso ilimitado a todo, similar a los globales. Maneje una de sus monstruosas bases de datos de 100 millones de filas (¡o más!), Y apreciará por qué no permiten que su motor DB contenga ningún golpe.

Pagan un precio por eso, un precio muy alto. Los DBA se ven obligados a ser casi patológicos con su atención al detalle, porque sus herramientas no los protegen. Lo mejor que tienen en cuanto a protección es ACID o quizás claves externas. Aquellos que no son patológicos se encuentran con un completo desastre de tablas que es completamente inutilizable, o incluso corrupto.

No es raro tener paquetes de software de 100k líneas. En teoría, cualquier línea en el software puede afectar a cualquier global en cualquier momento. En los DBA, nunca encontrará 100k consultas diferentes que puedan modificar la base de datos. Sería poco razonable mantenerlo con la atención a los detalles necesarios para protegerte de ti mismo. Si un DBA tiene algo grande como eso, encapsulará intencionalmente su base de datos utilizando accesores, esquivando los problemas "globales" y luego hará todo el trabajo posible a través de ese mecanismo "más seguro". Por lo tanto, cuando se trata de empujar, incluso las personas de la base de datos evitan los globales. Simplemente vienen con mucho peligro, y hay alternativas que son tan fuertes, pero no tan peligrosas.

¿Preferirías caminar sobre vidrios rotos o sobre aceras bien barridas, si todas las demás cosas son iguales? Sí, puedes caminar sobre cristales rotos. Sí, algunas personas incluso se ganan la vida haciéndolo. Pero aún así, ¡déjelos barrer la acera y seguir adelante!


0

Creo que la premisa es falsa. No hay razón para que una base de datos deba ser "estado global" en lugar de un objeto de contexto (muy grande). Si está vinculado a la base de datos particular que su código está utilizando a través de variables globales o parámetros de conexión de base de datos global fijos, no es diferente, y no menos malo, que cualquier otro estado global. Por otro lado, si pasa correctamente un objeto de contexto para la conexión de la base de datos, es solo un estado contextual grande (y ampliamente utilizado), no un estado global.

Medir la diferencia es fácil: ¿podría ejecutar dos instancias de la lógica de su programa, cada una utilizando su propia base de datos, en un solo programa / proceso sin realizar cambios invasivos en el código? Si es así, su base de datos no es realmente "estado global".


-2

Los globales no son malvados; Son simplemente una herramienta. El MAL USO de los globales es problemático, como lo es el mal uso de cualquier otra característica de programación.

Mi recomendación general es que los globales solo deben usarse en situaciones que se entienden y piensan bien, donde otras soluciones son menos óptimas. Lo más importante es que desea asegurarse de haber documentado bien dónde podría modificarse ese valor global, y si está ejecutando multiproceso, se está asegurando de que el acceso global y los globales dependientes sean dependientes de manera transaccional.


¿Le importaría a algunos de los que votan abajo explicar sus votos negativos? Parece grosero rechazar sin una explicación.
Byron Jones

-2

Patrón de solo lectura y suponga que sus datos no están actualizados cuando los imprime. Cola escribe o maneja conflictos de otra manera. Bienvenido al infierno demonio, estás usando db global.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.