it-swarm.com.de

Zwei Strukturen mit denselben Mitgliedern, aber unterschiedlicher Benennung. Ist das eine gute Idee?

Ich schreibe ein Programm, bei dem sowohl mit polaren als auch mit kartesischen Koordinaten gearbeitet wird.

Ist es sinnvoll, zwei verschiedene Strukturen für jede Art von Punkten zu erstellen, eine mit X und Y Mitgliedern und eine mit R und Theta Mitgliedern.

Oder ist es zu viel und es ist besser, nur eine Struktur mit first und second als Mitgliedern zu haben.

Was ich schreibe, ist einfach und es wird sich nicht viel ändern. Aber ich bin gespannt, was aus gestalterischer Sicht besser ist.

Ich denke, die erste Option ist besser. Es scheint besser lesbar zu sein und ich werde den Vorteil der Typprüfung erhalten.

Ich habe beide Lösungen gesehen, es ist also definitiv kontextabhängig.

Aus Gründen der Lesbarkeit ist es sehr effektiv, mehrere Strukturen zu haben, wie Sie vorschlagen. In einigen Umgebungen möchten Sie diese Strukturen jedoch häufig manipulieren, und Sie stellen fest, dass Sie Code duplizieren, z. B. Matrix * -Vektoroperationen. Es kann frustrierend werden, wenn die bestimmte Operation in Ihrem Vektorgeschmack nicht verfügbar ist, weil niemand sie dort portiert hat.

Die extreme Lösung (auf die wir schließlich verzichtet haben) besteht darin, eine Basisklasse mit CRTP-Vorlagen mit den Funktionen get <0> () get <1> () und get <2> () zu haben, um die Elemente auf generische Weise abzurufen. Diese Funktionen werden dann in der kartesischen oder polaren Struktur definiert, die von dieser Basisklasse abgeleitet ist. Es löst alle Probleme, hat aber einen ziemlich dummen Preis: Sie müssen die Metaprogrammierung von Vorlagen lernen. Wenn die Metaprogrammierung von Vorlagen für Ihr Projekt jedoch bereits ein faires Spiel ist, passt sie möglicherweise gut zusammen.

17
Cort Ammon

Ja, das macht sehr viel Sinn.

Der Wert einer Struktur besteht nicht nur darin, dass sie Daten unter einem handlichen Namen kapselt. Der Wert ist es kodifiziert Ihre Absichten, damit der Compiler Ihnen helfen kann, zu überprüfen, ob Sie eines Tages nicht gegen sie verstoßen (z. B. verwechseln Sie einen Polarkoordinatensatz mit einem kartesischen Koordinatensatz).

Die Leute sind schlecht darin, sich an solche kleinen Details zu erinnern, aber gut darin, mutige, erfinderische Pläne zu erstellen. Computer sind gut darin, Details zu verfälschen, und schlecht darin, kreative Pläne zu erstellen. Daher ist es immer eine gute Idee, so viele Details wie möglich auf den Computer zu verlagern, damit Sie frei an dem großen Plan arbeiten können.

113
Kilian Foth

Ja, obwohl sowohl kartesisch als auch polar (an ihrer Stelle) äußerst sinnvolle Koordinatendarstellungsschemata sind, sollten sie idealerweise niemals gemischt werden (wenn Sie einen kartesischen Punkt {1,1} haben, ist dies ein ganz anderer Punkt als Polar {1,1) }).

Abhängig von Ihren Anforderungen kann es sich auch lohnen, eine Koordinatenschnittstelle mit Methoden wie X(), Y(), Displacement() und Angle() ( oder möglicherweise Radius() und Theta(), abhängig von).

18
Vatine

Am Ende besteht das Ziel der Programmierung darin, Transistorbits umzuschalten, um nützliche Arbeit zu leisten. Ein so niedriges Denken würde jedoch zu unüberschaubarer Verrücktheit führen, weshalb es übergeordnete Programmiersprachen gibt, mit denen Sie die Komplexität verbergen können.

Wenn Sie nur eine Struktur mit Mitgliedern mit den Namen first und second erstellen, haben die Namen keine Bedeutung. Sie würden sie im Wesentlichen als Speicheradressen behandeln. Das macht den Zweck der Programmiersprache auf hoher Ebene zunichte.

Nur weil sie alle als double darstellbar sind, heißt das nicht, dass Sie sie austauschbar verwenden können. Zum Beispiel ist θ ein dimensionsloser Winkel, während y Längeneinheiten hat . Da die Typen nicht logisch ersetzbar sind, sollten sie zwei inkompatible Strukturen sein.

Wenn Sie wirklich Tricks zur Wiederverwendung von Speicher spielen müssen - und dies mit ziemlicher Sicherheit nicht tun sollten -, können Sie in C einen union -Typ verwenden um Ihre Absicht klar zu machen.

8
200_success

Erstens, haben beide explizit, gemäß @ Kilian-foths völlig vernünftiger Antwort.

Ich möchte jedoch hinzufügen:

Fragen Sie: Haben Sie wirklich Operationen, die für beide generisch sind, wenn sie als Paare von double betrachtet werden? Beachten Sie, dass dies nicht gleichbedeutend ist mit der Aussage, dass Sie Vorgänge haben, die für beide in ihren eigenen Begriffen gelten. Zum Beispiel kümmert sich 'plot (Coord)' darum, ob Coord polar oder kartesisch ist. Wenn Sie jedoch die Datei beibehalten, werden die Daten so behandelt, wie sie sind. Wenn Sie wirklich generische Operationen haben, sollten Sie entweder eine Basisklasse definieren oder einen Konverter für einen std::pair<double, double> Oder Tuple oder was auch immer Sie in Ihrer Sprache haben.

Ferner könnte ein Ansatz darin bestehen, einen Koordinatentyp als grundlegender und den anderen als bloße Unterstützung für Benutzer- oder externe Interaktion zu behandeln.

Sie können also sicherstellen, dass alle grundlegenden Operationen für Cartesian codiert sind, und dann die Konvertierung von Polar in Cartesian unterstützen. Dadurch wird vermieden, dass unterschiedliche Versionen vieler Vorgänge implementiert werden.

2
Keith

Eine mögliche Lösung, abhängig von der Sprache und wenn Sie wissen, dass beide Klassen ähnliche Methoden und Operationen haben, besteht darin, die Klasse einmal zu definieren und Typaliasnamen zu verwenden, um die Typen explizit unterschiedlich zu benennen.

Dies hat auch den Vorteil, dass Sie, solange die Klassen genau gleich sind, nur eine verwalten können. Sobald Sie sie jedoch ändern müssen, müssen Sie den Code nicht mehr ändern, da die Typen bereits verwendet wurden deutlich.

Eine andere Option, die wiederum von der Verwendung der Klassen abhängt (wenn Sie Polymorphismus und dergleichen benötigen), besteht darin, die öffentliche Vererbung für beide neuen Typen zu verwenden, sodass sie dieselbe öffentliche Schnittstelle wie der gemeinsame Typ haben, den sie beide darstellen. Dies ermöglicht auch eine getrennte Entwicklung der Typen.

1
Svalorzen

Ich glaube, dass es in diesem Fall eine schlechte Idee ist, dieselben Mitgliedsnamen zu haben, da dies den Code fehleranfälliger macht.

Stellen Sie sich das Szenario vor: Sie haben einige kartesische Punkte: pntA und pntB. Dann entscheiden Sie aus irgendeinem Grund, dass sie besser in Polarkoordinaten dargestellt werden sollen, und ändern die Deklaration und den Konstruktor.

Wenn alle Ihre Operationen nur Methodenaufrufe wären wie:

double distance = pntA.distanceFrom(pntB);

dann geht es dir gut Aber was ist, wenn Sie die Mitglieder explizit verwendet haben? Vergleichen Sie

double leftMargin = abs(pntA.x - pntB.x);
double leftMargin = abs(pntA.first - pntB.first);

Im ersten Fall wird der Code nicht kompiliert. Sie sehen den Fehler sofort und können ihn beheben. Wenn Sie jedoch dieselben Mitgliedsnamen haben, liegt der Fehler nur auf der logischen Ebene und ist viel schwerer zu erkennen.

Wenn Sie in einer nicht objektorientierten Sprache schreiben, ist es noch einfacher, eine falsche Struktur an die Funktion zu übergeben. Was hindert Sie daran, den folgenden Code zu schreiben?

double distance = calculate_distance_polar(cartesianPointA, polarPointB);

Andererseits könnten verschiedene Datentypen es Ihnen ermöglichen, den Fehler während der Kompilierung zu finden.

0
IMil