Zusammenfassung: Gibt es ein gutes Entwurfsmuster, um Doppelarbeit von Informationen zwischen eng voneinander abhängigen Werten zu reduzieren?
In meiner Arbeit ist es ziemlich üblich, eine Beziehung zwischen Mengen zu haben, so dass Sie eine der Mengen ableiten können, wenn Sie die anderen kennen. Ein Beispiel könnte das ideale Gasgesetz sein :
Pv = RT
Sie können sich vorstellen, eine Klasse zu erstellen, die den Zustand eines idealen Gases darstellt. Die Klasse würde 3 Eigenschaften hat, natürlich Pressure
, Temperature
und SpecificVolume
jeder einen entsprechenden Typen.
Für den Benutzer eines Objekts dieser Klasse erscheint es natürlich zu erwarten, dass Sie, wenn Sie Werte für beide Pressure
und festlegen Temperature
, einen Wert für auslesen SpecificVolume
und erwarten können, dass das Objekt diesen für Sie berechnet hat.
Wenn Sie Werte für beide Pressure
und festlegen SpecificVolume
, können Sie auch Temperature
usw. auslesen .
Um diese Klasse tatsächlich zu implementieren, sind jedoch einige Informationen doppelt vorhanden. Sie müssten alle Variationen der Gleichung explizit programmieren und jeweils eine andere Variable als abhängig behandeln:
T = P * v / R
P = R * T / v
v = R * T / P
das scheint das DRY-Prinzip zu verletzen . Obwohl jeder die gleiche Beziehung ausdrückt, erfordern diese Fälle eine unabhängige Codierung und Prüfung.
In realen Fällen ist die Logik, an die ich denke, komplexer als dieses Beispiel, weist jedoch das gleiche Grundproblem auf. Es wäre also ein echter Wert, wenn ich die Logik nur einmal oder zumindest weniger oft ausdrücken könnte.
Beachten Sie, dass eine Klasse wie diese wahrscheinlich auch sicherstellen muss, dass sie ordnungsgemäß initialisiert wurde, bevor Werte ausgelesen werden, aber ich denke, dass dies eine sekundäre Überlegung ist.
Obwohl ich ein mathematisches Beispiel gegeben habe, beschränkt sich die Frage nicht nur auf mathematische Beziehungen zwischen Daten. Das schien nur ein einfaches Beispiel zu sein, um den Punkt zu verdeutlichen.
quelle
Pv/T != R
. Ich bin mir nicht sicher, ob diese Idee Ihnen bei der Lösung der zugrunde liegenden Probleme helfen könnte oder ob dies sie sogar kompliziert.Antworten:
Aus OO-Sicht gibt es kein Problem. Sie könnten eine statische Klasse haben, die GasLaw-Methoden anbietet
und so weiter
und es würde kein Problem mit der Vervielfältigung geben. Es gibt kein Objekt, keine Datenelemente. Nur Verhaltensweisen, die alle unterschiedlich sind.
Sie können das Gasgesetz als "eine Sache" betrachten, aber die Operationen sind alle unterschiedlich. Es ist nichts Falsches daran, für jeden Anwendungsfall des Gesetzes eine Methode zu haben.
quelle
Dies ist nicht wirklich möglich, wie Sie fragen.
Bedenken Sie: Ich habe ein ideales Gasobjekt
g
. Wenn ich explizit alle drei Werte für Temperatur, Druck und spezifisches Volumen einstelle und dann die Temperatur erneut erhalte , wie z.sollte es:
t1
ich ursprünglich eingestellt habe?Wenn Sie dies tun müssen, müssen Sie für jedes Mitglied eine Menge Status nachverfolgen: Ist es nicht initialisiert, explizit durch den Clientcode festgelegt oder wird ein berechneter Wert zwischengespeichert? Wurde der explizit festgelegte oder zwischengespeicherte Wert von einem anderen Mitglied, das später festgelegt wird, ungültig gemacht?
Das Verhalten ist offensichtlich schwer vorherzusagen, wenn der Client-Code gelesen wird. Dies ist ein schlechtes Design, da es immer schwierig sein wird herauszufinden, warum Sie die Antwort erhalten haben, die Sie erhalten haben. Die Komplexität ist ein Zeichen dafür, dass dieses Problem nicht zu OO passt oder dass ein zustandsbehaftetes ideales Gasobjekt eine schlechte Wahl für die Abstraktion ist.
quelle
Erstens glaube ich nicht, dass Sie gegen das DRY-Prinzip verstoßen, da alle drei Formeln unterschiedliche Werte berechnen. Die Tatsache, dass es sich um eine Abhängigkeit zwischen allen drei Werten handelt, liegt daran, dass Sie sie als mathematische Gleichung und nicht als programmatische Variablenzuweisung betrachten.
Ich schlage vor, Ihren Fall mit einer unveränderlichen Klasse IdealGas wie folgt zu implementieren
Lassen Sie mich die Implementierung erklären. Die Klasse IdealGas kapselte die 3 Eigenschaften Druck, Temperatur und spezifisches Volumen als Endwerte für die Unveränderlichkeit. Jedes Mal, wenn Sie getXXX aufrufen, wird keine Berechnung durchgeführt. Der Trick besteht darin, 3 Konstruktoren für alle 3 Kombinationen von 2 gegebenen Parametern zu haben. In jedem Konstruktor berechnen Sie die fehlende dritte Variable. Die Berechnung wird einmal zur Konstruktionszeit durchgeführt und dem dritten Attribut zugewiesen. Da dies im Konstruktor erfolgt, kann das dritte Attribut endgültig und unveränderlich sein. Für die Unveränderlichkeit gehe ich davon aus, dass die Klassen Druck, Temperatur und spezifisches Volumen ebenfalls unveränderlich sind.
Der einzige verbleibende Teil besteht darin, Getter für alle Attribute zu haben. Es sind keine Setter vorhanden, da Sie Parameter an Konstruktoren übergeben. Wenn Sie ein Attribut ändern müssen, erstellen Sie eine neue IdealGas-Instanz mit den gewünschten Parametern.
In meinem Beispiel sind die Klassen Pressure, Temperature und SpecificVolume einfache Wrapper mit einem doppelten Wert. Der Beispielcode ist in Java, kann aber verallgemeinert werden.
Dieser Ansatz kann verallgemeinert werden, alle zugehörigen Daten an einen Konstruktor übergeben, verwandte Daten im Konstruktor berechnen und nur Getter haben.
quelle
Das ist eine wirklich interessante Frage! Sie haben im Prinzip Recht, dass Sie Informationen duplizieren, da in allen drei Fällen dieselbe Gleichung verwendet wird, nur mit unterschiedlichen Unbekannten.
In einer typischen Mainstream-Programmiersprache gibt es jedoch keine integrierte Unterstützung für das Lösen von Gleichungen. Variablen in der Programmierung sind immer bekannte Größen (zum Zeitpunkt der Ausführung), so dass Ausdrücke in Programmiersprachen trotz der oberflächlichen Ähnlichkeiten nicht mit mathematischen Gleichungen vergleichbar sind.
In einer typischen Anwendung wird eine Gleichung, wie Sie sie beschreiben, nur als drei separate Ausdrücke geschrieben, und Sie leben mit der Duplizierung. Es gibt jedoch Gleichungslösungsbibliotheken, die in einem solchen Fall verwendet werden könnten.
Dies ist jedoch mehr als ein Muster, es ist ein ganzes Programmierparadigma, das als Lösen von Einschränkungen bezeichnet wird, und es gibt spezielle Programmiersprachen wie Prolog für diese Art von Problemen.
quelle
Dies ist eine häufige Irritation beim Codieren mathematischer Modelle in Software. Beide verwenden eine sehr ähnliche Notation für einfache Modelle wie das Gasgesetz, aber Programmiersprachen sind keine Mathematik, und viele Dinge, die Sie bei der Arbeit in der Mathematikwelt weglassen können, müssen explizit in Software ausgeführt werden.
Es wiederholt sich nicht. In den meisten Programmiersprachen gibt es kein Konzept für Gleichung, algebraische Transformation oder "Auflösen nach x". Ohne eine Software-Darstellung all dieser Konzepte gibt es keine Möglichkeit für Software, zu einer
T = P * v / R
gegebenenPv = RT
Gleichung zu gelangen .Während es bei der Betrachtung einfacher Modelle wie dem Gasgesetz wie eine unangemessene Einschränkung der Programmiersprachen erscheint, erlaubt selbst eine etwas fortgeschrittenere Mathematik Gleichungen, die keine geschlossenen algebraischen Lösungen oder keine bekannte Methode haben, mit der die Lösung effizient abgeleitet werden kann. Für die Softwarearchitektur konnte man sich im komplexeren Fall nicht darauf verlassen.
Und der einfache Fall für sich? Ich bin mir nicht sicher, ob es sich überhaupt lohnt, die Spezifikation der Funktion in der Programmiersprache zu lesen. Modelle werden in der Regel ersetzt, nicht modifiziert und haben normalerweise nur wenige zu lösende Variablen.
quelle
Nein, zwei sind genug. Aber machen sie privat und setzen Methoden wie
pressure()
,temperature()
undspecificVolume()
. Eine davon, die nicht den beiden privaten Eigenschaften entspricht, sollte die entsprechende Logik haben. So können wir die Datenverdoppelung beseitigen.Es sollten drei Konstrukteuren mit Parametern sein
(P, T)
,(P, v)
und(T, v)
. Eine davon, die den beiden privaten Eigenschaften entspricht, fungiert einfach als Setter. Die anderen beiden sollten die entsprechende Logik haben. Natürlich wird die Logik dreimal geschrieben (zweimal hier und einmal im vorherigen Absatz), aber es handelt sich nicht um Duplikate. Auch wenn Sie sie als Duplikate betrachten, werden sie benötigt.Ja mathematisch und nein OO-ly. In OO-Objekten sind erstklassige Bürger keine Ausdrücke. Um erstklassige Bürger auszudrücken (oder um die Beziehung durch dasselbe auszudrücken), müssen wir beispielsweise eine Klasse schreiben,
Expression
oderEquation
das ist keine leichte Aufgabe (es kann einige Bibliotheken geben).Beziehung ist auch kein erstklassiger Bürger. Dafür müssen wir möglicherweise eine Klasse schreiben,
Relationship
die auch nicht einfach ist. Das Leben mit Duplikaten ist jedoch einfacher.Wenn Sie sich für Duplikate entscheiden und die Parameter in der Beziehung hoch sind, sollten Sie das Builder- Muster verwenden. Auch wenn die Parameter weniger berücksichtigt werden, sollten Sie statische Faktoren verwenden , um Einschränkungen der Konstruktorüberladung zu vermeiden.
quelle
Eine Klasse sollte das Verhalten eines idealen Gases darstellen. Eine Klasseninstanz - ein Objekt - repräsentiert den Zustand.
Dies ist keine Nissen pflücken. Das Klassendesign sollte aus Sicht des Verhaltens und der Funktionalität erfolgen. Das Verhalten wird öffentlich zugänglich gemacht, so dass ein instanziiertes Objekt (ja, das ist überflüssig) durch Ausübung von Verhalten einen Zustand erreicht.
Damit:
Und das Folgende ist einfach Unsinn und in der realen Welt unmöglich. Und mach es auch nicht im Code:
Durch das Verhalten wird die Frage "Doppelte Informationen" in Frage gestellt
Das Beeinflussen derselben Statuseigenschaften durch verschiedene Methoden ist keine Duplizierung. Das Folgende wirkt sich auch auf die Temperatur aus, ist jedoch kein Duplikat der oben genannten:
quelle
Möglicherweise können Sie die Codegenerierung verwenden, um die verschiedenen Formen der Gleichung aus der von Ihnen angegebenen Einzelform zu generieren.
Ich kann mir vorstellen, dass es für komplexere Formeln ziemlich schwierig wäre. Aber für die einfache, die Sie geben, müssen Sie nur
Ich kann mir vorstellen, dass Sie, wenn Sie dieser Liste Multiplikation, Addition und Subtraktion hinzufügen und Ihre Basisformel jede Variable nur einmal enthält, mithilfe der Zeichenfolgenmanipulation automatisch alle Versionen der Formel mit dem entsprechenden Boilerplate-Code generieren können, um die richtige Version unter Berücksichtigung der bekannten Variablen zu verwenden .
Wolfram Alpha verfügt beispielsweise über verschiedene Werkzeuge zur Manipulation von Gleichungen.
Jedoch!! Wenn Sie keine großen Listen dieser Klassen erstellen müssen, kann ich solchen Code nicht als effektive Nutzung Ihrer Zeit ansehen.
Sie müssen diesen Code nur einmal pro Funktion generieren, und Ihre Funktionen sind wahrscheinlich zu komplex, um so einfach wie Ihr Beispiel gelöst zu werden.
Das manuelle Codieren jeder Version ist wahrscheinlich die schnellste und zuverlässigste Methode zum Generieren der Funktionen. Obwohl Sie sich eine Lösung vorstellen können, bei der Sie codieren, um Werte iterativ zu erraten und die Wahrheit zu testen, müssen Sie die zu berechnenden Funktionen generieren und kompilieren die unbekannte Variable mit einer vernünftigen Menge an Rechenleistung.
quelle
Ich denke, Sie überdenken dies. Betrachten Sie die folgende Lösung:
Sie müssen nur eine Funktion definieren, keine Duplizierung, Sie können diese sogar für eine interne Klassenimplementierung verwenden, oder Sie können diese Funktion einfach verwenden, indem Sie einfach ändern, welche Parameter Sie wo verwenden.
Sie können dasselbe Muster auch für jede Situation verwenden, in der Sie eine Funktion haben, bei der Werte vertauscht werden.
Wenn Sie eine statisch typisierte Sprache verwenden und sich mit einer Funktion befassen mussten, bei der Werte unterschiedliche Typen haben können, können Sie die Vorlagenprogrammierung verwenden (Hinweis: Verwenden der C ++ - Syntax)
quelle