Was ist, wenn Globale Sinn machen?

10

Ich habe einen Wert, den viele Objekte benötigen. Zum Beispiel eine Finanzanwendung mit unterschiedlichen Anlagen als Objekten, von denen die meisten den aktuellen Zinssatz benötigen.

Ich hatte gehofft, mein "finanzielles Umfeld" als Objekt mit dem Zinssatz als Eigentum zusammenzufassen. Geschwisterobjekte, die diesen Wert benötigen, können ihn jedoch nicht erreichen.

Wie teile ich Werte zwischen vielen Objekten, ohne mein Design zu überkoppeln? Offensichtlich denke ich falsch darüber nach.

Greg
quelle
2
Ist der Zinssatz für die Dauer Ihrer Berechnungen festgelegt oder führen Sie eine Simulation durch, bei der er zwischen den Zeitschritten variieren kann?
James
Es ist eher eine Simulation - es kann sich während des Laufs ändern.
Greg
updateMuss in diesem Fall wirklich jede Investition den Zinssatz speichern oder kann sie ihn über einen Parameter an eine Funktion senden, die bei jedem Zeitschritt aufgerufen wird? Können Sie im Pseudocode posten, wie Ihre Simulation funktioniert?
James
3
Sie sind einer der richtigen Tracks, a Singletonist ein Global mit OO syntaktischem Zucker und eine schreckliche Lösung, die Ihren Code auf die schlechteste Art und Weise eng miteinander verbindet. Lesen Sie diesen Artikel immer wieder, bis Sie ihn verstanden haben!
Der Zinssatz ist wie eine Zeitreihe, bei der eine Funktion DateTimeals Eingabe verwendet und eine Zahl als Ausgabe zurückgegeben wird.
Rwong

Antworten:

14

Ich habe einen Wert, den viele Objekte benötigen.

Dies ist ein Designgeruch. Es ist ungewöhnlich, dass viele Objekte etwas wissen müssen. Der aktuelle Zinssatz ist jedoch ein ziemlich gutes Beispiel für außergewöhnliche Umstände. Eine Sache, über die man sich Sorgen machen muss, ist, dass es selten den Zinssatz gibt. Unterschiedliche Finanzinstrumente verwenden unterschiedliche Zinssätze. Zumindest verwenden unterschiedliche Länder unterschiedliche Standardtarife. Um das Testen und Berichten zu erleichtern, möchten Sie normalerweise eine Rate übergeben, da Sie dort nicht die aktuelle Rate verwenden möchten . Sie möchten die Rate "Was wäre wenn" oder "Zum Stichtag" verwenden.

Wie teile ich Werte zwischen vielen Objekten, ohne mein Design zu überkoppeln?

Wenn Sie sie freigeben und nicht alle auf eine einzelne Instanz verweisen. Um die gleiche Sache Passing Kupplung ist nach wie vor zu einem gewissen Grad, aber nicht über so etwas wie der aktuellen Zins Kopplung da als Eingabe für eine Vielzahl von Berechnungen benötigt wird.

Telastyn
quelle
17
Das Problem mit außergewöhnlichen Umständen ist, dass sie in der realen Software nicht so außergewöhnlich sind.
Mattnz
Ich denke, das ist ein ernstes Missverständnis. Unsere Algorithmen existieren gleichzeitig in verschiedenen Kontexten. Der erste ist der globale Kontext. Dann "dieser" Kontext für objektorientierte Sprachen. Sitzungskontext in einem Webdienst, Transaktionskontext in einer DB-Umgebung, Hauptfenster für eine GUI, ... und es gibt Informationen, die zu diesen Kontexten gehören. Sie müssen "rumhängen" und verfügbar sein, die gleiche Menge (Objekte) für jeden im gleichen Kontext. Das ist okay Das Problem besteht darin, dies für jedes Objekt zu lösen, nicht durch Erstellen eines Kontextdienstes oder Verwenden eines Frameworks wie Spring in Java.
Lorand Kedves
3
Der aktuelle Zinssatz ist nichts Außergewöhnliches. Es gibt viele Beispiele für Gegenstände aus der realen Welt mit einem Wert: - Geschwindigkeitsbegrenzung auf offener Straße, akzeptabler Blutalkoholspiegel, GST-Steuersatz (oder Mehrwertsteuersatz), um nur einige zu nennen. Das ist der Unterschied zwischen Wissenschaft und Technik - Ingenieure lösen die heutigen Probleme der realen Welt. Wissenschaftler träumen von dem Tag, an dem die reale Welt in schöne Kisten der Perfektion passt und diese Probleme löst.
Mattnz
1
Ich habe dies als Antwort gewählt, weil es einfach ist und nicht auf ein Jahrzehnt OOP-Erfahrung angewiesen ist, um zu groken. Vielen Dank an alle Befragten. Ich hatte dank der vielen Referenzen einen ganzen Tag Zeit zum Lesen, bin aber immer noch etwas ratlos. Bei einer so einfachen Frage war ich überrascht, wie vielfältig und emotional die Antworten waren. Ich bin weiterhin davon überzeugt, dass es manchmal eine zentrale Quelle für globale, aber unterschiedliche Daten gibt, die am besten von einem Singleton bereitgestellt werden. Ich würde nicht denken, dass man Zeiger in einer Hierarchie von Objekten nach oben und unten übergeben sollte, nur um einen Singleton zu vermeiden. Nochmals vielen Dank an alle.
Greg
@mattnz, das Problem ist, dass jedes einzelne Ihrer Beispiele in Fällen variabel ist, in denen Sie Ihr Programm an mehrere Benutzerbasen verteilen, die sich über Unternehmen, Bundesstaaten oder Länder erstrecken können. Alle können auch über die Zeit variabel sein.
Dan Lyons
10

In diesem speziellen Fall würde ich das Singleton-Muster verwenden . Die FinancialEnvironment wäre das Objekt, das allen anderen Klassenbibliotheken bekannt ist, würde jedoch vom Singleton instanziiert. Idealerweise würden Sie dieses instanziierte Objekt dann an die verschiedenen Klassenbibliotheken senden.

Zum Beispiel:

  • Service Layer (Klassenbibliothek) - Instanziiert das FinancialEnvironment-Objekt über einen Singleton
  • Business Logic Layer (Klassenbibliothek) - Akzeptiert das FinancialEnvironment-Objekt aus der Service-Schicht
  • Datenzugriffsschicht (Klassenbibliothek) - Akzeptiert das FinancialEnvironment-Objekt von der Service-Schicht (oder abhängig von Ihrer Architektur die Business Logic-Schicht). Oder der Singleton ruft die Datenzugriffsschicht auf, um Informationen wie den Zinssatz aus einem Repository (Datenbank / Webdienst / WCF-Dienst) abzurufen.
  • Klassenbibliothek für Entitäten (oder DTOs, wenn Sie es so nennen möchten) - Wo sich das FinancialEnvironment-Objekt befindet. Alle anderen Klassenbibliotheken haben einen Verweis auf die Entities-Klassenbibliothek.

Die anderen Klassen sind nur über die Entities-Klassenbibliothek miteinander verbunden. Sie akzeptieren ein instanziiertes FinancialEnvironment-Objekt. Es ist ihnen egal, wie es erstellt wurde, nur die Service-Schicht, alles, was sie wollen, sind die Informationen. Der Singleton könnte auch intelligent genug sein, um mehrere FinancialEnvironment-Objekte zu speichern, abhängig von den Regeln für das lokale Objekt, wie @Telastyn hervorhob.

Nebenbei bemerkt, ich bin kein großer Fan des Singleton-Musters, ich betrachte es als Code-Geruch, da es sehr leicht missbraucht werden kann. Aber in einigen Fällen brauchen Sie es.

Aktualisieren:

Wenn Sie unbedingt eine globale Variable haben müssen, funktioniert die Implementierung des Singleton-Musters wie oben beschrieben. Ich bin jedoch kein großer Fan davon, und basierend auf den Kommentaren aus meinem ursprünglichen Beitrag sind es auch einige andere Leute nicht. So volatil wie eine InterestRate ist ein Singleton möglicherweise nicht die beste Lösung. Singletons funktionieren am besten, wenn sich die Informationen nicht ändern. Zum Beispiel habe ich in einer meiner Anwendungen einen Singleton verwendet, um Leistungsindikatoren zu instanziieren. Denn wenn sie sich ändern, müssen Sie über eine Logik verfügen, um die zu aktualisierenden Daten verarbeiten zu können.

Wenn ich ein Wettmann wäre, würde ich wetten, dass der Zinssatz irgendwo in einer Datenbank gespeichert oder über einen Webdienst abgerufen wurde. In diesem Fall wird ein Repository (Datenzugriffsschicht) empfohlen, um diese Informationen abzurufen. Um unnötige Reisen in die Datenbank zu vermeiden (ich bin nicht sicher, wie oft sich die Zinssätze ändern, oder andere Informationen in der FinancialInformation-Klasse), könnte Caching verwendet werden. In der C # -Welt funktioniert die Caching Application Block-Bibliothek von Microsoft sehr gut.

Das einzige, was sich gegenüber dem obigen Beispiel ändern würde, wären die verschiedenen Klassen in der Serviceschicht, die die FinancialInformation benötigen, die von der Datenzugriffsschicht abgerufen werden, anstatt dass Singleton das Objekt instanziiert.

bwalk2895
quelle
8
Pfui. Das Globale zu einem Singleton zu machen, macht es nicht weniger stinkend. Wenn überhaupt, haben Sie sich so viel mehr eingeschränkt.
Telastyn
3
@DavidCowden Es spielt keine Rolle, ob die Implementierung nicht komplex ist. Sie fubar Ihr Design schlechter als globale. Sie sind global und erzwingen (nicht benötigte) Einschränkungen, dass Sie nur eine haben.
Telastyn
4
Ich wollte einen sarkastischen Kommentar veröffentlichen, in dem es heißt: "Mach es zu einem Singleton und es wird von schlechter Praxis zu bester Praxis", aber dann sah ich, dass es bereits eine akzeptierte und hochgewählte Antwort war. Sehr schön!
Kevin
4
Singletonist ein Global mit OO syntaktischem Zucker und einer Krücke für Faule und Schwache. Singleton/globalist der absolut schlechteste Weg, Ihren Code eng mit etwas zu koppeln , das später Krebs sein wird, wenn Sie erkennen, was für eine kolossal schlechte Idee es war und warum alle sagen, dass sie es sind!
4
@Telastyn: Es ist eine unglückliche Realität, dass die perfektesten Designs, wenn sie die perfekt geordnete Welt des theoretischen Software-Designs verlassen und sich der realen Welt anschließen, begeistert sind.
Mattnz
4

Konfigurationsdateien?

Wenn Sie Werte haben, die "global" verwendet werden, fügen Sie diese bitte in eine Konfigurationsdatei ein. Dann kann jedes System und Subsystem darauf verweisen und die benötigten Schlüssel ziehen, um sie schreibgeschützt zu machen.

Dunkle Nacht
quelle
Der Benutzer muss also jedes Mal eine Konfigurationsdatei aktualisieren, wenn sich der Zinssatz ändert?
Caleb
2
Warum nicht? hängt natürlich von der "Variablen" ab. Dinge, die sich häufig ändern, sollten in einem CENTRALIZED-Datenspeicher abgelegt werden.
Darknight
1

Ich spreche aus der Erfahrung von jemandem, der ungefähr einen Monat lang an einem Projekt mit guter Größe (~ 50.000 LOC) gewartet hat, das wir gerade veröffentlicht haben.

Ich kann Ihnen sagen, dass Sie wahrscheinlich kein globales Objekt wollen. Die Einführung dieser Art von Cruft bietet viel mehr Möglichkeiten für Missbrauch als es hilft.

Mein erster Vorschlag ist, dass wenn Sie mehrere verschiedene Klassen haben, die einen aktuellen Zinssatz benötigen, Sie wahrscheinlich nur möchten, dass sie einen IInterestRateConsumeroder etwas implementieren . Innerhalb dieser Schnittstelle haben Sie eine SetCurrentInterestRate(double rate)(oder was auch immer Sinn macht) oder vielleicht nur eine Eigenschaft.

Das Weitergeben eines Zinssatzes ist keine Kopplung. Wenn Ihre Klasse einen Zinssatz benötigt, ist dies Teil der API. Es ist nur dann eine Kopplung, wenn sich eine Ihrer Klassen Gedanken darüber macht, wie die andere Klasse diesen Zinssatz verwendet.

Wayne Werner
quelle
Einen Zinssatz weiterzugeben ist eine Kopplung, es ist einfach keine schlechte Kopplung.
Vaughandroid
1

Martin Fowler hat einen Artikel , der kurz darüber spricht, wie man eine statische globale in etwas Flexibleres umgestaltet. Grundsätzlich machen Sie es zu einem Singleton und ändern dann den Singleton so, dass er das Überschreiben der Klasse der Instanz mit einer Unterklasse unterstützt (und verschieben bei Bedarf die Logik, die die Instanz erstellt, in eine separate Klasse, die untergeordnet werden kann, was Sie tun würden Wenn die Superklasseninstanz erstellt wird, ist das spätere Ersetzen ein Problem.

Natürlich müssen Sie die Probleme mit Singletons (sogar austauschbaren Singletons) gegen den Schmerz abwägen, überall dasselbe Objekt zu passieren.

Was das Objekt "Finanzielle Umgebung" betrifft, ist es praktisch, beim ersten Durchgang zu programmieren, aber wenn Sie fertig sind, haben Sie einige zusätzliche Abhängigkeiten hinzugefügt. Klassen, die nur einen Zinssatz benötigen, funktionieren jetzt nur, wenn ein Objekt für das finanzielle Umfeld übergeben wurde. Dies macht die Wiederverwendung schwierig, wenn kein Objekt für das finanzielle Umfeld herumliegt. Daher würde ich davon abraten, es weit zu verbreiten.

psr
quelle
0

Warum nicht die Zinsdaten in einen zentralen Cache stellen?

Sie können eine von mehreren Cache-Bibliotheken verwenden, je nachdem, was Ihren Anforderungen am besten entspricht. Mit memcached werden alle Probleme mit der Parallelität und der Codeverwaltung gelöst, und Ihre Anwendung kann auf mehrere Prozesse skaliert werden.

Oder gehen Sie das ganze Schwein und speichern Sie sie in einer Datenbank, die es Ihnen ermöglicht, auf mehrere Server zu skalieren.

James Anderson
quelle
0

In solchen Situationen habe ich den Begriff "Kontext" mit manchmal mehreren Ebenen erfolgreich eingeführt (wiederverwendet).

Dies bedeutet einen Singleton, also einen "globalen" Objektspeicher, von dem diese Art von Objekten angefordert werden kann. Codes, die sie erfordern, enthalten den Header des Geschäfts und verwenden die globalen Funktionen, um ihre Objektinstanzen abzurufen (wie jetzt der Zinsanbieter).

Der Laden kann entweder sein:

  • streng typisiert: Sie fügen die Header für alle bereitgestellten Typen ein und können so typisierte Accessoren wie InterestRate getCurrentInterestRate () erstellen.
  • oder generisch: Object getObject (enum obType); und erweitern Sie die obType-Enumeration nur mit den neuen Arten (obtypeCurrentInterestRate).

Je größer das System ist, desto benutzerfreundlicher ist die letztere Lösung für ein recht geringes Risiko, die falsche Aufzählung zu verwenden. Auf der anderen Seite können Sie mit Sprachen, die Forward-Typdeklarationen ermöglichen, typisierte Accessoren verwenden, ohne alle Header im Store einzuschließen.

Noch ein Hinweis: Möglicherweise haben Sie mehrere Instanzen desselben Objekttyps für unterschiedliche Zwecke, z. B. manchmal unterschiedliche Sprachwerte für die GUI sowie für Ausdrucks-, globale und Protokolle auf Sitzungsebene usw., sodass der Name der Aufzählung / des Zugriffs NICHT den tatsächlichen Typ widerspiegeln sollte , aber die Rolle der angeforderten Instanz (CurrentInterestRate).

In der Store-Implementierung müssen Sie die Kontextebenen und Kontextinstanzsammlungen verwalten. Ein einfaches Beispiel ist der Webdienst, bei dem Sie den globalen Kontext (eine Instanz für alle Anforderungen für dieses Objekt - problematisch bei einer Serverfarm) und einen Kontext für jede Websitzung haben. Sie können auch Kontexte für jeden Benutzer haben, der mehrere parallele Sitzungen usw. haben kann. Bei mehreren Servern sollten Sie für solche Dinge eine Art verteilten Cache verwenden.

Wenn die Anforderung eingeht, entscheiden Sie, auf welcher Kontextebene sich das angeforderte Objekt befindet, und rufen diesen Kontext für den Aufruf ab. Wenn das Objekt vorhanden ist, senden Sie es zurück. Wenn nicht, erstellen und speichern Sie es auf dieser Kontextebene und geben es zurück. Synchronisieren Sie natürlich den Erstellungsabschnitt (und veröffentlichen Sie ihn im verteilten Cache). Die Erstellung kann wie ein Plugin konfiguriert werden, am besten mit Sprachen, die das Erstellen von Objektinstanzen anhand ihres Klassennamens (Java, Objective C, ...) ermöglichen. Sie können dies jedoch auch in C mit steckbaren Bibliotheken mit Factory-Funktionen tun.

Randnotiz: Der Aufrufer sollte NICHT zu viel über seine eigenen Kontexte und die Kontextebene des angeforderten Objekts wissen. Gründe: 1: Es ist leicht, Fehler (oder "clevere Tricks") zu machen, wenn man mit diesen Parametern spielt. 2: Die Kontextebene der angeforderten kann sich später ändern. Ich verbinde meistens Kontextinformationen mit dem Thread, sodass der Objektspeicher die Informationen ohne zusätzliche Parameter aus der Anforderung enthält.

Auf der anderen Seite kann die Anfrage einen Hinweis für die Instanz enthalten: wie das Abrufen des Zinssatzes für ein bestimmtes Datum. Es sollte derselbe "globale" Zugriff sein, aber je nach Datum mehrere Instanzen (und unterschiedliche Datumswerte zu derselben Instanz zwischen Ratenänderungen führen). Daher ist es ratsam, der Anforderung ein "Hinweis" -Objekt hinzuzufügen, das von der Instanz Fabrik und nicht das Geschäft; und einen KeyForHint zur Fabrik, der vom Geschäft verwendet wird. Sie können diese Funktionen später hinzufügen, wie ich gerade erwähnt habe.

Für Ihren Fall ist dies eine Art Overkill (nur ein Objekt wird auf globaler Ebene bereitgestellt), aber für einen recht kleinen und einfachen zusätzlichen Code erhalten Sie derzeit einen Mechanismus für weitere, möglicherweise komplexere Anforderungen.

Eine weitere gute Nachricht: Wenn Sie in Java sind, erhalten Sie diesen Service von Spring, ohne zu viel darüber nachzudenken. Ich wollte ihn nur im Detail erklären.

Lorand Kedves
quelle
0

Der Grund, KEIN globales (oder Singleton) zu verwenden, ist, dass es oft überraschend nützlich ist, denselben Code mehrmals im selben Programm verwenden zu können, obwohl Sie anfänglich nur einen Wert erwarten, zum Beispiel:

  • zu berechnen, was passieren würde, wenn der Zinssatz anders wäre
  • Einige Komponenten hängen vom US-Zinssatz ab, und einige Komponenten hängen vom britischen Zinssatz ab

Ich würde den Zinssatz zu einem Mitglied der Klasse "Finanzinstrument" machen und akzeptieren, dass Sie ihn an alle Mitgliedsklassen weitergeben müssen (entweder pro Berechnung oder indem Sie ihnen beim Bau einen Zeiger / Haken geben).

Jack V.
quelle
0

Dinge sollten in Nachrichten übergeben werden, nicht von einem globalen Ding gelesen werden.

Tulains Córdova
quelle