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.
object-oriented
Greg
quelle
quelle
update
Muss 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?Singleton
ist 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!DateTime
als Eingabe verwendet und eine Zahl als Ausgabe zurückgegeben wird.Antworten:
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.
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.
quelle
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:
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.
quelle
Singleton
ist ein Global mit OO syntaktischem Zucker und einer Krücke für Faule und Schwache.Singleton/global
ist 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!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.
quelle
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
IInterestRateConsumer
oder etwas implementieren . Innerhalb dieser Schnittstelle haben Sie eineSetCurrentInterestRate(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.
quelle
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.
quelle
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.
quelle
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:
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.
quelle
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:
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).
quelle
Dinge sollten in Nachrichten übergeben werden, nicht von einem globalen Ding gelesen werden.
quelle