it-swarm.com.de

Wie unterscheiden sich Globals von einer Datenbank?

Ich bin gerade auf diese alte Frage gestoßen und habe gefragt, was am globalen Zustand so böse ist, und die am besten gewählte, akzeptierte Antwort besagt, dass Sie keinem Code vertrauen können, der mit globalen Variablen funktioniert, weil es einen anderen gibt Code irgendwo anders könnte kommen und seinen Wert ändern, und dann wissen Sie nicht, wie sich Ihr Code verhalten wird, weil die Daten unterschiedlich sind! Aber Wenn ich mir das ansehe, kann ich nicht anders, als zu denken, dass dies eine wirklich schwache Erklärung ist, denn wie unterscheidet sich das von der Arbeit mit Daten, die in einer Datenbank gespeichert sind?

Wenn Ihr Programm mit Daten aus einer Datenbank arbeitet, ist es Ihnen egal, ob anderer Code in Ihrem System diese ändert oder ob ein ganz anderes Programm sie ändert. Es ist Ihnen egal, was die Daten sind; das ist der ganze Punkt. Alles, was zählt, ist, dass Ihr Code korrekt mit den Daten umgeht, auf die er trifft. (Natürlich beschönige ich hier das oft heikle Thema des Caching, aber lassen Sie uns das für den Moment ignorieren.)

Wenn die Daten, mit denen Sie arbeiten, aus einer externen Quelle stammen, auf die Ihr Code keinen Einfluss hat, z. B. einer Datenbank (oder Benutzereingaben, einem Netzwerk-Socket oder einer Datei usw.) und nichts falsch ist Wie sind dann globale Daten im Code selbst - über die Ihr Programm viel mehr Kontrolle hat - irgendwie eine schlechte Sache, wenn sie offensichtlich weit weniger schlecht sind als ganz normale Dinge, die niemand als Problem ansieht?

253
Mason Wheeler

Erstens würde ich sagen, dass die Antwort, auf die Sie verweisen, dieses spezielle Problem überbewertet und dass das Hauptübel des globalen Staates darin besteht, dass es auf unvorhersehbare Weise eine Kopplung einführt, die es schwierig machen kann, das Verhalten Ihres Systems in Zukunft zu ändern.

Wenn Sie sich jedoch weiter mit diesem Thema befassen, gibt es Unterschiede zwischen dem globalen Status in einer typischen objektorientierten Anwendung und dem Status, der in einer Datenbank gespeichert ist. Kurz gesagt, die wichtigsten davon sind:

  • Objektorientierte Systeme ermöglichen das Ersetzen eines Objekts durch eine andere Objektklasse, sofern es sich um einen Subtyp des ursprünglichen Typs handelt. Dadurch kann Verhalten geändert werden, nicht nur Daten.

  • Der globale Status in einer Anwendung bietet normalerweise nicht die starken Konsistenzgarantien, die eine Datenbank bietet - es gibt keine Transaktionen, bei denen Sie einen konsistenten Status dafür sehen, keine atomaren Aktualisierungen usw.

Darüber hinaus können wir den Datenbankstatus als notwendiges Übel ansehen. Es ist unmöglich, es aus unseren Systemen zu entfernen. Ein globaler Zustand ist jedoch nicht erforderlich. Wir können es vollständig beseitigen. Selbst wenn die Probleme mit einer Datenbank genauso schlimm wären, können wir einige der potenziellen Probleme beseitigen, und eine Teillösung ist besser als keine Lösung.

118
Jules

Was sind die Probleme mit globalen Variablen, basierend auf der akzeptierten Antwort auf die von Ihnen verknüpfte Frage?

Sehr kurz, es macht den Programmzustand unvorhersehbar.

Datenbanken sind die meiste Zeit ACID-konform. ACID befasst sich speziell mit den zugrunde liegenden Problemen, die einen Datenspeicher unvorhersehbar oder unzuverlässig machen würden.

Darüber hinaus beeinträchtigt der globale Status die Lesbarkeit Ihres Codes.

Dies liegt daran, dass globale Variablen in einem Bereich vorhanden sind, der weit von ihrer Verwendung entfernt ist, möglicherweise sogar in einer anderen Datei. Wenn Sie eine Datenbank verwenden, verwenden Sie einen Datensatz oder ein ORM-Objekt, das lokal für den Code ist, den Sie lesen (oder sollten).

Datenbanktreiber bieten normalerweise eine konsistente, verständliche Schnittstelle für den Zugriff auf Daten, die unabhängig von der Problemdomäne identisch sind. Wenn Sie Daten aus einer Datenbank abrufen, verfügt Ihr Programm über ein Kopie der Daten. Updates sind atomar. Im Gegensatz zu globalen Variablen, bei denen mehrere Threads oder Methoden möglicherweise ohne Atomizität mit demselben Datenelement arbeiten, es sei denn, Sie fügen selbst eine Synchronisierung hinzu. Aktualisierungen der Daten sind unvorhersehbar und schwer zu finden. Aktualisierungen können verschachtelt sein, was zu Standardlehrbuchbeispielen für die Beschädigung von Multithread-Daten führt (z. B. verschachtelte Inkremente).

Datenbanken modellieren in der Regel zunächst andere Daten als globale Variablen. Abgesehen davon werden Datenbanken von Grund auf als ACID-kompatibler Datenspeicher konzipiert, der viele der Probleme mit globalen Variablen mindert.

75
user22815

Ich würde ein paar Beobachtungen machen:

Ja, eine Datenbank hat den globalen Status.

Tatsächlich ist es ein superglobaler Staat, wie Sie betont haben. Es ist universal! Sein Umfang umfasst alles oder jedermann , das eine Verbindung zur Datenbank herstellt . Und ich vermute, dass viele Leute mit langjähriger Erfahrung Ihnen Horrorgeschichten darüber erzählen können, wie "seltsame Dinge" in den Daten zu "unerwartetem Verhalten" in einer oder mehreren der relevanten Anwendungen geführt haben ...

Eine der möglichen Konsequenzen der Verwendung einer globalen Variablen besteht darin, dass zwei unterschiedliche "Module" diese Variable für ihre eigenen unterschiedlichen Zwecke verwenden. Insofern ist eine Datenbanktabelle nicht anders. Es kann dem gleichen Problem zum Opfer fallen.

Hmm ... Hier ist das Ding:

Wenn ein Modul in irgendeiner Weise nicht extrinsisch arbeitet, tut es nichts.

Ein nützliches Modul kann gegeben Daten oder find sein. Und es kann return Daten oder ändern Status. Aber wenn es nicht in irgendeiner Weise mit der Außenwelt interagiert, kann es genauso gut nichts tun.

Jetzt bevorzugen wir empfangen Daten und return Daten. Die meisten Module sind einfach einfacher zu schreiben, wenn sie unter völliger Missachtung der Aktivitäten der Außenwelt geschrieben werden können. Aber letztendlich muss etwas find die Daten und modifizieren das Externe, globaler Staat.

Darüber hinaus sind die Daten in realen Anwendungen vorhanden, sodass sie durch verschiedene Operationen gelesen und aktualisiert werden können. Einige Probleme werden durch Sperren und Transaktionen verhindert. Um zu verhindern, dass diese Operationen miteinander in Konflikt stehen im Prinzip , muss am Ende des Tages einfach sorgfältiges Denken. ( Und Fehler machen ...)

Aber wir arbeiten im Allgemeinen auch nicht direkt mit dem globalen Status.

Sofern sich die Anwendung nicht in der Datenschicht befindet (in SQL oder was auch immer), sind die Objekte, mit denen unsere Module arbeiten, tatsächlich copy des gemeinsam genutzten globalen Status. Wir können tun, was wir wollen, ohne dass dies Auswirkungen auf den tatsächlichen, gemeinsam genutzten Zustand. hat

Und in Fällen, in denen wir diesen globalen Zustand mutieren müssen, können wir unter der Annahme, dass sich die Daten, die wir erhalten haben, nicht geändert haben, im Allgemeinen die gleiche Art von Sperrung durchführen, die wir für unsere lokalen globalen Daten durchführen würden.

nd schließlich machen wir normalerweise andere Dinge mit Datenbanken als wir könnte mit ungezogenen Globalen.

Eine freche, kaputte Welt sieht so aus:

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

Wir verwenden einfach keine Datenbanken für solche In-Process-/Operational-Dinge. Und es könnte die Langsamkeit der Datenbank und die relative Bequemlichkeit einer einfachen Variablen sein, die uns abschreckt: Unsere träge, umständliche Interaktion mit Datenbanken macht sie einfach zu schlechten Kandidaten für viele der Fehler, die wir historisch mit Variablen. gemacht haben

45
svidgen

Ich bin mit der grundsätzlichen Behauptung nicht einverstanden, dass:

Wenn Ihr Programm mit Daten aus einer Datenbank arbeitet, ist es Ihnen egal, ob anderer Code in Ihrem System diese ändert oder ob ein ganz anderes Programm sie ändert.

Mein erster Gedanke war "Wow. Just Wow". Es wird so viel Zeit und Mühe aufgewendet, um genau dies zu vermeiden - und herauszufinden, welche Kompromisse und Kompromisse für jede Anwendung funktionieren. Es einfach zu ignorieren ist ein Rezept für eine Katastrophe.

Aber ich bin auch auf architektonischer Ebene einverstanden. Eine globale Variable ist nicht nur ein globaler Zustand. Es ist ein globaler Zustand, auf den von überall transparent zugegriffen werden kann. Im Gegensatz zur Verwendung einer Datenbank benötigen Sie ein Handle - (es sei denn, Sie speichern als Handle in einer globalen Variablen ....)

Die Verwendung einer globalen Variablen könnte beispielsweise so aussehen

int looks_ok_but_isnt() {
  return global_int++;
}

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

Dasselbe mit einer Datenbank zu tun, müsste jedoch expliziter sein, was sie tut

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);
   ...
}

Die Datenbank ist offensichtlich mit einer Datenbank durcheinander. Wenn Sie keine Datenbank verwenden möchten, können Sie den expliziten Status verwenden, der fast genauso aussieht wie der Datenbankfall.

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);
   ...
}

Ich würde also argumentieren, dass die Verwendung einer Datenbank viel mehr der Verwendung eines expliziten Status als der Verwendung globaler Variablen entspricht.

21

Der Punkt, dass der einzige Grund, warum globale Variablen nicht vertrauenswürdig sind, da der Status an einer anderen Stelle geändert werden kann, an sich nicht Grund genug ist, sie nicht zu verwenden, stimmte zu (es ist jedoch ein ziemlich guter Grund!). Es ist wahrscheinlich, dass die Antwort hauptsächlich die Verwendung beschrieb, bei der es sinnvoller wäre, den Zugriff einer Variablen auf nur Codebereiche zu beschränken, mit denen sie sich befasst.

Datenbanken sind jedoch eine andere Sache, da sie sozusagen für den Zugriff "global" konzipiert sind.

Zum Beispiel:

  • Datenbanken verfügen normalerweise über eine integrierte Typ- und Strukturvalidierung, die über die Sprache hinausgeht, auf die auf sie zugegriffen wird
  • Datenbanken werden fast einstimmig basierend auf Transaktionen aktualisiert, wodurch inkonsistente Zustände verhindert werden, bei denen nicht garantiert werden kann, wie der Endzustand in einem globalen Objekt aussehen wird (es sei denn, er ist hinter einem Singleton versteckt).
  • Die Datenbankstruktur wird zumindest implizit anhand der Tabellen- oder Objektstruktur dokumentiert, mehr als die Anwendung, die sie verwendet

Am wichtigsten ist jedoch, dass Datenbanken einem anderen Zweck dienen als eine globale Variable. Datenbanken dienen zum Speichern und Durchsuchen großer Mengen organisierter Daten, wobei globale Variablen bestimmte Nischen bedienen (sofern dies gerechtfertigt ist).

18
Jeffrey Sweeney

Aber wenn ich mir das ansehe, kann ich nicht anders, als zu denken, dass das eine wirklich schwache Erklärung ist, denn wie unterscheidet sich das von der Arbeit mit Daten, die in einer Datenbank gespeichert sind?

Oder anders als bei der Arbeit mit einem interaktiven Gerät, mit einer Datei, mit gemeinsamem Speicher usw. Ein Programm, das bei jeder Ausführung genau dasselbe tut, ist ein sehr langweiliges und ziemlich nutzloses Programm. Also ja, es ist ein schwaches Argument.

Für mich besteht der Unterschied, der sich auf globale Variablen auswirkt, darin, dass sie verborgene und ungeschützte Kommunikationslinien bilden. Das Lesen von einer Tastatur ist sehr offensichtlich und geschützt. Ich muss einen bestimmten Funktionsaufruf ausführen und kann nicht auf den Tastaturtreiber zugreifen. Gleiches gilt für den Dateizugriff, den gemeinsam genutzten Speicher und Ihre Beispieldatenbanken. Für den Leser des Codes ist es offensichtlich, dass diese Funktion von der Tastatur liest, dass diese Funktion auf eine Datei zugreift, dass eine andere Funktion auf den gemeinsam genutzten Speicher zugreift (und dass dies besser geschützt werden sollte) und dass eine andere Funktion auf eine Datenbank zugreift.

Bei globalen Variablen ist dies dagegen überhaupt nicht offensichtlich. Die API fordert auf, foo(this_argument, that_argument) aufzurufen. Die aufrufende Sequenz enthält nichts, was besagt, dass die globale Variable g_DangerWillRobinson Auf einen bestimmten Wert gesetzt werden sollte, jedoch vor dem Aufruf von foo (oder nach dem Aufruf von foo).


Google hat die Verwendung von nicht konstanten Referenzargumenten in C++ in erster Linie verboten, weil es für den Leser des Codes nicht offensichtlich ist, dass foo(x)x ändert, weil foo a benötigt nicht konstante Referenz als Argument. (Vergleiche mit C #, das vorschreibt, dass sowohl die Funktionsdefinition als auch die Aufrufseite einen Referenzparameter mit dem Schlüsselwort ref qualifizieren müssen.) Obwohl ich dem Google-Standard in dieser Hinsicht nicht zustimme, verstehe ich deren Punkt.

Code wird einmal geschrieben und einige Male geändert, aber wenn er überhaupt gut ist, wird er viele, viele Male gelesen. Versteckte Kommunikationswege sind sehr schlechtes Karma. Die nicht konstante Referenz von C++ repräsentiert eine kleine versteckte Kommunikationslinie. Eine gute API oder eine gute IDE zeigt mir, dass "Oh! Dies ist ein Aufruf als Referenz". Globale Variablen sind eine riesige versteckte Kommunikationslinie.

10
David Hammen

Ich denke, dass die zitierte Erklärung das Problem so stark vereinfacht, dass die Argumentation lächerlich wird. Natürlich trägt der Status einer externen Datenbank zum globalen Status bei. Die wichtige Frage ist wie Ihr Programm hängt vom (veränderlichen) globalen Zustand ab. Wenn eine Bibliotheksfunktion zum Teilen von Zeichenfolgen im Leerraum von in einer Datenbank gespeicherten Zwischenergebnissen abhängen würde, würde ich diesem Entwurf mindestens genauso widersprechen wie einem globalen Zeichenarray, das für denselben Zweck verwendet wird. Wenn Sie jedoch entscheiden, dass Ihre Anwendung zu diesem Zeitpunkt kein vollständiges DBMS zum Speichern von Geschäftsdaten benötigt und eine globale speicherinterne Schlüsselwertstruktur ausreicht, ist dies nicht unbedingt ein Zeichen für ein schlechtes Design. Wichtig ist, dass - unabhängig davon, welche Lösung Sie zum Speichern Ihrer Daten auswählen - diese Auswahl auf einen sehr kleinen Teil des Systems beschränkt ist, sodass die meisten Komponenten unabhängig von der für die Bereitstellung ausgewählten Lösung sein und isoliert und bereitgestellt auf Einheitentests getestet werden können Die Lösung kann zu einem späteren Zeitpunkt mit geringem Aufwand geändert werden.

8
5gon12eder

Als Softwareentwickler, der hauptsächlich mit eingebetteter Firmware arbeitet, verwende ich fast immer globale Variablen für alles, was zwischen Modulen wechselt. In der Tat ist es Best Practice für Embedded. Sie werden statisch zugewiesen, sodass kein Risiko besteht, dass der Heap/Stack durchgebrannt wird, und es wird keine zusätzliche Zeit für die Stapelzuweisung/-bereinigung beim Ein-/Ausstieg von Funktionen benötigt.

Der Nachteil dabei ist, dass wir überlegen müssen, wie diese Variablen verwendet werden, und vieles davon hängt von der gleichen Art von Gedanken ab, die es gibt in Datenbank-Wrangling. Jedes asynchrone Lesen/Schreiben von Variablen MUSS atomar sein. Wenn mehr als eine Stelle eine Variable schreiben kann, müssen einige Überlegungen angestellt werden, um sicherzustellen, dass immer gültige Daten geschrieben werden, damit das vorherige Schreiben nicht willkürlich ersetzt wird (oder dass willkürliches Ersetzen eine sichere Sache ist). Wenn dieselbe Variable mehr als einmal gelesen wird, müssen einige Überlegungen angestellt werden, was passiert, wenn die Variable den Wert zwischen den Lesevorgängen ändert, oder wenn zu Beginn eine Kopie der Variablen erstellt werden muss, damit die Verarbeitung unter Verwendung eines konsistenten Werts erfolgt, auch wenn Dieser Wert wird während der Verarbeitung veraltet.

(Für das letzte Mal sah sich das Softwareteam an meinem allerersten Tag eines Vertrags mit einem Flugzeug-Gegenmaßnahmensystem, das so sicherheitsrelevant war, einen Fehlerbericht an, den sie seit etwa einer Woche herausfinden wollten. Ich hatte gerade genug Zeit, um die Entwickler-Tools und eine Kopie des Codes herunterzuladen. Ich fragte "Könnte diese Variable zwischen den Lesevorgängen nicht aktualisiert werden und sie verursachen?", Bekam aber keine wirkliche Antwort. Hey, was bedeutet das? Immerhin ein neuer Typ? Also, während sie noch darüber diskutierten, fügte ich Schutzcode hinzu, um die Variable atomar zu lesen, machte einen lokalen Build und sagte im Grunde "Hey Leute, probier das aus". Ein Weg, um zu beweisen, dass ich meine Vertragsrate wert war . :)

Globale Variablen sind also keine eindeutig schlechte Sache, aber sie lassen Sie für eine Vielzahl von Problemen offen, wenn Sie nicht sorgfältig darüber nachdenken.

8
Graham

Abhängig davon, welchen Aspekt Sie beurteilen, sind globale Variablen und Datenbankzugriff möglicherweise Welten voneinander entfernt. Solange wir sie jedoch als Abhängigkeiten beurteilen, sind sie gleich.

Betrachten wir die Definition einer reinen Funktion durch die funktionale Programmierung, die ausschließlich von den Parametern abhängen muss, die sie als Eingaben verwendet, um eine deterministische Ausgabe zu erzeugen. Das heißt, wenn zweimal dieselbe Argumentation gegeben wird, muss das gleiche Ergebnis erzielt werden.

Wenn eine Funktion von einer globalen Variablen abhängt, kann sie nicht mehr als rein betrachtet werden, da sie für dieselbe Menge oder dieselben Argumente unterschiedliche Ausgaben liefern kann, da sich der Wert der globalen Variablen zwischen den Aufrufen möglicherweise geändert hat.

Die Funktion kann jedoch immer noch als deterministisch angesehen werden, wenn wir die globale Variable genauso als Teil der Funktionsschnittstelle betrachten wie ihre anderen Argumente, sodass dies nicht das Problem ist. Das Problem ist nur, dass dies bis zu dem Moment verborgen ist, in dem wir überrascht durch unerwartetes Verhalten von scheinbar offensichtlichen Funktionen sind. Lesen Sie dann deren Implementierungen, um die versteckten Abhängigkeiten zu entdecken .

Dieser Teil, der Moment, in dem eine globale Variable zu einer versteckten Abhängigkeit wird, wird von uns Programmierern als böse angesehen. Es macht es schwieriger, über den Code nachzudenken, es ist schwer vorherzusagen, wie er sich verhält, es ist schwer wiederzuverwenden, es ist schwer zu testen, und insbesondere erhöht es die Debug- und Fixierungszeit, wenn ein Problem auftritt.

Das gleiche passiert, wenn wir die Abhängigkeit von der Datenbank verbergen. Wir können Funktionen oder Objekte haben, die Datenbankabfragen und -befehle direkt aufrufen, diese Abhängigkeiten verbergen und genau die gleichen Probleme verursachen, die globale Variablen verursachen. oder wir können sie explizit machen, was, wie sich herausstellt, als Best Practice angesehen wird, die viele Namen trägt, wie z. B. Repository-Muster, Datenspeicher, Gateway usw.

P.: Es gibt andere Aspekte, die für diesen Vergleich wichtig sind, z. B. ob es sich um Parallelität handelt, aber dieser Punkt wird hier durch andere Antworten abgedeckt.

7
MichelHenrich

Okay, fangen wir mit dem historischen Punkt an.

Wir befinden uns in einer alten Anwendung, die in einer typischen Mischung aus Assembly und C geschrieben ist. Es gibt keine Funktionen, nur Prozeduren. Wenn Sie ein Argument übergeben oder einen Wert von einer Prozedur zurückgeben möchten, verwenden Sie eine globale Variable. Es ist unnötig zu erwähnen, dass dies ziemlich schwer nachzuverfolgen ist, und im Allgemeinen kann jede Prozedur mit jeder globalen Variablen tun, was sie will. Es ist nicht überraschend, dass die Leute Argumente übergeben und Werte auf andere Weise zurückgeben, sobald dies möglich ist (es sei denn, es war leistungskritisch, dies nicht zu tun - z. B. den Quellcode der Build Engine (Duke 3D)). Der Hass auf globale Variablen wurde hier geboren - Sie hatten nur sehr wenig Ahnung, welchen globalen Status jede Prozedur lesen und ändern würde, und Sie konnten Prozeduraufrufe nicht wirklich sicher verschachteln.

Bedeutet dies, dass globaler variabler Hass der Vergangenheit angehört? Nicht ganz.

Zunächst muss ich erwähnen, dass ich das genau den gleichen Ansatz gesehen habe, um Argumente in dem Projekt zu übergeben, an dem ich gerade arbeite. Zum Übergeben von zwei Referenztypinstanzen in C # in einem Projekt, das ungefähr 10 Jahre alt ist. Es gibt buchstäblich keinen guten Grund, dies so zu tun, und es ist höchstwahrscheinlich entweder auf Frachtkult oder auf ein völliges Missverständnis der Funktionsweise von C # zurückzuführen.

Der größere Punkt ist, dass Sie durch Hinzufügen globaler Variablen den Umfang jedes einzelnen Codeteils erweitern, der Zugriff auf diese globale Variable hat. Erinnern Sie sich an all diese Empfehlungen wie "Halten Sie Ihre Methoden kurz"? Wenn Sie 600 globale Variablen haben (wieder ein Beispiel aus der Praxis: /), werden alle Ihre Methodenbereiche implizit um diese 600 globalen Variablen erweitert, und es gibt keine einfache Möglichkeit, zu verfolgen, wer Zugriff auf was hat.

Wenn dies falsch gemacht wird (wie üblich :)), können globale Variablen untereinander gekoppelt sein. Sie haben jedoch keine Ahnung, wie sie gekoppelt sind, und es gibt keinen Mechanismus, um sicherzustellen, dass der globale Zustand immer konsistent ist. Selbst wenn Sie kritische Abschnitte einführen, um die Konsistenz zu gewährleisten, werden Sie feststellen, dass der Vergleich mit einer richtigen ACID-Datenbank schlecht ist:

  • Es gibt keine Möglichkeit, ein Teilupdate zurückzusetzen, es sei denn, Sie behalten die alten Werte vor der "Transaktion" bei. Zu diesem Zeitpunkt ist es natürlich schon ein Gewinn, einen Wert als Argument zu übergeben :)
  • Jeder, der auf denselben Status zugreift muss, muss denselben Synchronisierungsprozess einhalten. Aber es gibt keine Möglichkeit, dies durchzusetzen - wenn Sie vergessen, den kritischen Abschnitt einzurichten, sind Sie fertig.
  • Selbst wenn Sie den gesamten Zugriff korrekt synchronisieren, gibt es möglicherweise verschachtelte Aufrufe, die auf den teilweise geänderten Status zugreifen. Dies bedeutet, dass Sie entweder einen Deadlock haben (wenn Ihre kritischen Abschnitte nicht erneut eingegeben werden) oder mit inkonsistenten Daten umgehen (wenn sie erneut eingegeben werden).

Ist es möglich, diese Probleme zu lösen? Nicht wirklich. Sie benötigen eine Kapselung, um damit umzugehen, oder wirklich strenge Disziplin. Es ist schwer, die Dinge richtig zu machen, und das ist im Allgemeinen kein sehr gutes Erfolgsrezept für die Softwareentwicklung :)

Ein kleinerer Bereich erleichtert das Nachdenken über Code. Globale Variablen machen selbst die einfachsten Codeteile zu einem riesigen Bereich.

Dies bedeutet natürlich nicht, dass globales Scoping böse ist. Es sollte einfach nicht die erste Lösung sein, für die Sie sich entscheiden - es ist ein typisches Beispiel für "einfach zu implementieren, schwer zu warten".

6
Luaan

Eine globale Variable ist ein Werkzeug, sie kann zum Guten und zum Bösen verwendet werden.

Eine Datenbank ist ein Werkzeug, sie kann zum Guten und zum Bösen verwendet werden.

Wie das Originalplakat feststellt, ist der Unterschied nicht allzu groß.

Unerfahrene Schüler denken oft, dass Fehler etwas sind, das anderen Menschen passiert. Lehrer verwenden "Globale Variablen sind böse" als vereinfachten Grund, schlechtes Design zu bestrafen. Die Schüler verstehen im Allgemeinen nicht, dass nur weil ihr 100-Zeilen-Programm fehlerfrei ist, nicht die gleichen Methoden für 10000-Zeilen-Programme verwendet werden können.

Wenn Sie mit Datenbanken arbeiten, können Sie den globalen Status nicht einfach verbieten, da es darum geht. Stattdessen erhalten Sie weitere Detailrichtlinien wie ACID und Normal Forms und so weiter.

Wenn Menschen den ACID-Ansatz für globale Variablen verwenden würden, wären sie nicht so schlecht.

Wenn Sie Datenbanken jedoch schlecht entwerfen, können dies Albträume sein.

6
Stig Hemmer

Einige der anderen Antworten versuchen zu erklären, warum die Verwendung einer Datenbank gut ist. Sie liegen falsch! Eine Datenbank ist ein globaler Zustand und als solcher genauso böse wie ein Singleton oder eine globale Variable. Es ist völlig falsch, eine Datenbank zu verwenden, wenn Sie stattdessen einfach eine lokale Karte oder ein Array verwenden können!

Globale Variablen ermöglichen einen globalen Zugriff, der das Missbrauchsrisiko birgt. Globale Variablen haben auch Vorteile. Globale Variablen sollten im Allgemeinen vermieden werden, nicht etwas, das Sie niemals verwenden sollten. Wenn Sie sie leicht vermeiden können, sollten Sie sie vermeiden. Aber wenn die Vorteile die Nachteile überwiegen, sollten Sie sie natürlich nutzen! *

Das gleiche gilt ** für Datenbanken, die einen globalen Status haben - genau wie globale Variablen. Wenn Sie ohne Zugriff auf eine Datenbank auskommen können und die resultierende Logik alles tut, was Sie benötigen, und ebenso komplex ist, erhöht die Verwendung einer Datenbank das Risiko für Ihr Projekt ohne entsprechenden Nutzen.

Im wirklichen Leben erfordern viele Anwendungen einen globalen Status, manchmal sogar einen dauerhaften globalen Status - deshalb haben wir Dateien, Datenbanken usw.


* Die Ausnahme hier sind Studenten. Es ist sinnvoll, den Schülern die Verwendung globaler Variablen zu verbieten, damit sie lernen müssen, welche Alternativen es gibt.

** Einige Antworten behaupten fälschlicherweise, dass Datenbanken irgendwie besser geschützt sind als andere Formen des globalen Zustands (die Frage bezieht sich explizit auf globaler Zustand, nicht nur auf globale Variablen). Das sind Blödsinn. Der im Datenbankszenario angebotene primäre Schutz erfolgt gemäß Konvention, der für jeden anderen globalen Status genau gleich ist. Die meisten Sprachen bieten auch viel zusätzlichen Schutz für den globalen Status in Form von const, Klassen, die es einfach nicht erlauben, ihren Status zu ändern, nachdem er im Konstruktor festgelegt wurde, oder Getter und Setter, die Thread-Informationen aufnehmen können oder Programmstatus berücksichtigt.

5
Peter

Für mich ist das Hauptübel, dass Globals keinen Schutz vor Parallelitätsproblemen haben. Sie können Mechanismen hinzufügen, um solche Probleme mit Globals zu behandeln. Je mehr Parallelitätsprobleme Sie lösen, desto mehr ahmen Ihre Globals eine Datenbank nach. Das sekundäre Übel ist kein Nutzungsvertrag.

5
G DeMasters

In gewissem Sinne ähnelt die Unterscheidung zwischen globalen Variablen und einer Datenbank der Unterscheidung zwischen privaten und öffentlichen Mitgliedern eines Objekts (vorausgesetzt, jemand verwendet noch öffentliche Felder). Wenn Sie sich das gesamte Programm als Objekt vorstellen, sind die Globals die privaten Variablen und die Datenbank die öffentlichen Felder.

Der Hauptunterschied liegt hier in der übernommenen Verantwortung.

Wenn Sie ein Objekt schreiben, wird davon ausgegangen, dass jeder, der die Mitgliedsmethoden verwaltet, sicherstellt, dass sich private Felder weiterhin gut verhalten. Sie geben jedoch bereits alle Annahmen über den Zustand der öffentlichen Felder auf und behandeln sie mit besonderer Sorgfalt.

Die gleiche Annahme gilt auf einer breiteren Ebene für die globale v/s-Datenbank. Außerdem garantiert die Programmiersprache/das Ökosystem die Zugriffsbeschränkungen für private v/s public in derselben Weise, in der sie in der globalen v/s-Datenbank (nicht gemeinsam genutzter Speicher) erzwungen werden.

Wenn Multithreading ins Spiel kommt, ist das Konzept der privaten v/s öffentlichen v/s globalen v/s-Datenbank lediglich eine Unterscheidung entlang eines Spektrums.

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
        }
    }
}
2
Benito Ciaro

Eine Datenbank kann ein globaler Zustand sein, muss aber nicht immer sein. Ich bin nicht einverstanden mit der Annahme, dass Sie keine Kontrolle haben. Eine Möglichkeit, dies zu verwalten, ist das Sperren und die Sicherheit. Dies kann im Datensatz, in der Tabelle oder in der gesamten Datenbank erfolgen. Ein anderer Ansatz besteht darin, ein Versionsfeld zu haben, das das Ändern eines Datensatzes verhindert, wenn die Daten veraltet sind.

Wie bei einer globalen Variablen können die Werte in einer Datenbank nach dem Entsperren geändert werden. Es gibt jedoch viele Möglichkeiten, den Zugriff zu steuern (Geben Sie nicht allen Entwicklern das Kennwort für das Konto, mit dem Daten geändert werden dürfen.). Wenn Sie eine Variable mit eingeschränktem Zugriff haben, ist diese nicht sehr global.

1
JeffO

Natürlich sind Globals nicht immer unangemessen. Sie existieren, weil sie eine legitime Verwendung haben. Das Hauptproblem bei Globals und die Hauptquelle der Ermahnung, sie zu vermeiden, besteht darin, dass Code, der ein Global verwendet, an dieses und nur ein Global angehängt ist.

Stellen Sie sich beispielsweise einen HTTP-Server vor, auf dem der Servername gespeichert ist.

Wenn Sie den Servernamen in einem globalen Namen speichern, kann der Prozess keine Logik für zwei verschiedene Servernamen gleichzeitig ausführen. Vielleicht hat das ursprüngliche Design nie in Betracht gezogen, mehr als eine Serverinstanz gleichzeitig auszuführen, aber wenn Sie später entscheiden, dass Sie dies tun möchten, können Sie dies einfach nicht, wenn der Servername global ist.

Wenn sich der Servername dagegen in einer Datenbank befindet, gibt es kein Problem. Sie können einfach eine Instanz dieser Datenbank für jede Instanz des HTTP-Servers erstellen. Da jede Instanz des Servers eine eigene Instanz der Datenbank hat, kann sie einen eigenen Servernamen haben.

Der primäre Einwand gegen Globals ist, dass es nur einen Wert für den gesamten Code geben kann, der auf diesen Global zugreift. Dies gilt nicht für Datenbankeinträge. Der gleiche Code kann problemlos auf verschiedene Datenbankinstanzen zugreifen, die unterschiedliche Werte für einen bestimmten Eintrag haben.

0
David Schwartz

Ich denke, die Prämisse ist falsch. Es gibt keinen Grund, warum eine Datenbank ein "globaler Status" sein muss und kein (sehr großes) Kontextobjekt. Wenn Sie über globale Variablen oder feste globale Datenbankverbindungsparameter an die bestimmte Datenbank binden, die Ihr Code verwendet, ist dies nicht anders und nicht weniger schlimm als jeder andere globale Status. Wenn Sie dagegen ein Kontextobjekt für die Datenbankverbindung ordnungsgemäß weitergeben, handelt es sich nur um einen großen (und weit verbreiteten) Kontextstatus, nicht um einen globalen Status.

Das Messen des Unterschieds ist einfach: Könnten Sie zwei Instanzen Ihrer Programmlogik, die jeweils eine eigene Datenbank verwenden, in einem einzigen Programm/Prozess ausführen, ohne invasive Änderungen am Code vorzunehmen? Wenn ja, ist Ihre Datenbank nicht wirklich "globaler Status".

Ich denke, dies ist eine interessante Frage, aber es ist ein wenig schwierig zu beantworten, da es zwei Hauptprobleme gibt, die unter dem Begriff "globaler Staat" zusammengefasst werden. Das erste ist das Konzept der "globalen Kopplung". Der Beweis dafür ist, dass die für den globalen Zustand gegebene Alternative die Abhängigkeitsinjektion ist. Die Sache ist, dass DI nicht unbedingt den globalen Zustand beseitigt. Das heißt, es ist absolut möglich und üblich, Abhängigkeiten vom globalen Zustand einzufügen. DI entfernt die Kopplung, die mit globalen Variablen und dem häufig verwendeten Singleton-Muster geliefert wird. Abgesehen von einem etwas weniger offensichtlichen Design gibt es kaum Nachteile bei der Beseitigung dieser Art von Kopplung, und die Vorteile der Beseitigung der Kopplung nehmen exponentiell mit der Anzahl der Abhängigkeiten von diesen Globalen zu.

Der andere Aspekt ist der gemeinsame Zustand. Ich bin mir nicht sicher, ob es eine wirklich klare Unterscheidung zwischen global geteiltem Staat und geteiltem Staat im Allgemeinen gibt, aber die Kosten und Nutzen sind viel differenzierter. Einfach ausgedrückt gibt es unzählige Softwaresysteme, für deren Nützlichkeit ein gemeinsamer Status erforderlich ist. Bitcoin zum Beispiel ist eine sehr clevere Möglichkeit, den Status global (buchstäblich) dezentral zu teilen. Das ordnungsgemäße Teilen des veränderlichen Zustands ohne große Engpässe ist schwierig, aber nützlich. Wenn Sie dies also nicht wirklich tun müssen, können Sie Ihre Anwendung vereinfachen, indem Sie den gemeinsam genutzten veränderlichen Status minimieren.

Die Frage, wie sich Datenbanken von globalen unterscheiden, ist also auch über diese beiden Aspekte verteilt. Führen sie eine Kopplung ein? Ja, das können sie, aber es hängt stark davon ab, wie die Anwendung und die Datenbank gestaltet sind. Es gibt zu viele Faktoren, um eine einzige Antwort darauf zu erhalten, ob Datenbanken eine globale Kopplung ohne Details des Designs einführen. Die Frage, ob sie die gemeinsame Nutzung von Staaten einführen, ist der Hauptpunkt einer Datenbank. Die Frage ist, ob sie es gut machen. Auch hier denke ich, dass dies zu kompliziert ist, um ohne viele andere Informationen wie Alternativen und viele andere Kompromisse zu antworten.

0
JimmyJames

Ich würde es etwas anders sehen: "Globale Variable" wie Verhalten ist ein Preis, den Datenbankadministratoren (DBAs) zahlen, weil es ein notwendiges Übel ist, ihre Arbeit zu erledigen.

Das Problem mit globalen Variablen ist, wie mehrere andere betont haben, kein willkürliches. Das Problem ist, dass ihre Verwendung das Verhalten Ihres Programms immer weniger vorhersehbar macht, da es schwieriger wird zu bestimmen, wer die Variable auf welche Weise verwendet. Dies ist ein großes Problem für moderne Software, da moderne Software normalerweise viele, viele flexible Aufgaben ausführen muss. Es kann im Verlauf eines Laufs Milliarden oder sogar Billionen komplexer staatlicher Manipulationen durchführen. Die Fähigkeit, wahre Aussagen darüber zu beweisen, was diese Software in diesen Milliarden oder Billionen von Operationen leisten wird, ist äußerst wertvoll.

Bei moderner Software bieten alle unsere Sprachen Tools, die dies unterstützen, z. B. die Kapselung. Die Entscheidung, es nicht zu benutzen, ist unnötig, was zu der Mentalität "Globale sind böse" führt. In vielen Regionen der Softwareentwicklung werden sie nur von Personen verwendet, die nicht wissen, wie sie besser codieren können. Dies bedeutet, dass sie nicht nur direkt Probleme verursachen, sondern indirekt darauf hinweisen, dass der Entwickler nicht wusste, was sie taten. In anderen Regionen sind Globals völlig normal (insbesondere eingebettete Software liebt Globals, auch weil sie gut mit ISRs funktionieren). Inmitten der vielen Softwareentwickler da draußen sind sie jedoch die Minderheitsstimme. Die einzige Stimme, die Sie hören, ist "Globale sind böse".

Die Datenbankentwicklung ist eine dieser Minderheitensituationen. Die für die DBA-Arbeit benötigten Tools sind sehr leistungsfähig und ihre Theorie ist nicht in der Kapselung verwurzelt. Um jeden einzelnen Leistungsschub aus ihren Datenbanken herauszuholen, benötigen sie uneingeschränkten Zugriff auf alles, ähnlich wie bei globalen Datenbanken. Verwenden Sie eine ihrer monströsen Datenbanken mit 100 Millionen Zeilen (oder mehr!), Und Sie werden es zu schätzen wissen, warum sie ihre DB-Engine keine Schläge halten lassen.

Sie zahlen einen Preis dafür, einen lieben Preis. Datenbankadministratoren sind gezwungen, mit Liebe zum Detail fast pathologisch zu sein, da ihre Tools sie nicht schützen. Das Beste, was sie zum Schutz haben, sind Säuren oder vielleicht Fremdschlüssel. Diejenigen, die nicht pathologisch sind, finden sich mit einer völligen nordnung von Tabellen wieder, die völlig unbrauchbar oder sogar korrupt sind.

Es ist nicht ungewöhnlich, 100.000-Zeilen-Softwarepakete zu haben. Theoretisch kann jede Zeile in der Software zu jedem Zeitpunkt eine globale Zeile beeinflussen. In Datenbankadministratoren finden Sie niemals 100.000 verschiedene Abfragen, mit denen die Datenbank geändert werden kann. Es wäre unvernünftig, dies mit der Liebe zum Detail beizubehalten, die erforderlich ist, um Sie vor sich selbst zu schützen. Wenn ein DBA so etwas Großes hat, kapselt er seine Datenbank absichtlich mithilfe von Accessoren, umgeht die "globalen" Probleme und erledigt dann so viel Arbeit wie möglich durch diesen "sichereren" Mechanismus. Wenn Push zum Einsatz kommt, vermeiden sogar die Datenbankmitarbeiter globale Daten. Sie sind einfach mit einer großen Gefahr verbunden, und es gibt Alternativen, die nur so stark, aber nicht so gefährlich sind.

Möchten Sie lieber auf Glasscherben oder auf schön gekehrten Gehwegen herumlaufen, wenn alle anderen Dinge gleich sind? Ja, Sie können auf Glasscherben gehen. Ja, manche Leute verdienen sogar ihren Lebensunterhalt damit. Aber lassen Sie sie trotzdem den Bürgersteig fegen und weitermachen!

0
Cort Ammon

Es gibt verschiedene Unterschiede:

  • Ein Datenbankwert kann kann im laufenden Betrieb geändert werden. Der Wert eines globalen Codes, der im Code festgelegt ist, kann nicht geändert werden, es sei denn, Sie stellen Ihre Anwendung erneut bereit und ändern Ihren Code. In der Tat ist dies beabsichtigt. Eine Datenbank ist für Werte gedacht, die sich im Laufe der Zeit ändern können, aber globale Variablen sollten nur für Dinge sein, die sich niemals ändern werden und wenn sie keine tatsächlichen Daten enthalten.

  • Ein Datenbankwert (Zeile, Spalte) hat einen Kontext und eine relationale Zuordnung in der Datenbank. Diese Beziehung kann einfach mit Tools wie beispielsweise Jailer extrahiert und analysiert werden. Eine globale Variable ist dagegen etwas anders. Sie können alle Verwendungen finden, aber es wäre unmöglich für Sie, mir all die Art und Weise zu sagen, wie die Variable mit dem Rest Ihrer Welt interagiert.

  • Globale Variablen sind schneller. Um etwas aus einer Datenbank zu erhalten, muss eine Datenbankverbindung hergestellt, eine Auswahl für mich ausgeführt und dann die Datenbankverbindung geschlossen werden. Darüber hinaus werden alle Typkonvertierungen benötigt, die Sie möglicherweise benötigen. Vergleichen Sie dies mit einem globalen Zugriff in Ihrem Code.

Dies sind die einzigen, an die ich momentan denken kann, aber ich bin mir sicher, dass es noch mehr gibt. Einfach ausgedrückt, sie sind zwei verschiedene Dinge und sollten für verschiedene Ziele verwendet werden.

0
Arnab Datta