Ich denke der Titel "Methodenflags als Argumente oder als Mitgliedsvariablen?" mag suboptimal sein, aber da mir eine bessere Terminologie fehlt, geht es weiter:
Ich versuche gerade, mich mit dem Problem auseinanderzusetzen, ob Flags für eine bestimmte ( private ) Klassenmethode als Funktionsargumente oder über eine Mitgliedsvariable übergeben werden sollen und / oder ob es ein Muster oder einen Namen gibt, der diesen Aspekt und / oder abdeckt ob dies auf andere Designprobleme hindeutet.
Zum Beispiel (Sprache könnte C ++, Java, C # sein, spielt meiner Meinung nach keine Rolle):
class Thingamajig {
private ResultType DoInternalStuff(FlagType calcSelect) {
ResultType res;
for (... some loop condition ...) {
...
if (calcSelect == typeA) {
...
} else if (calcSelect == typeX) {
...
} else if ...
}
...
return res;
}
private void InteralStuffInvoker(FlagType calcSelect) {
...
DoInternalStuff(calcSelect);
...
}
public void DoThisStuff() {
... some code ...
InternalStuffInvoker(typeA);
... some more code ...
}
public ResultType DoThatStuff() {
... some code ...
ResultType x = DoInternalStuff(typeX);
... some more code ... further process x ...
return x;
}
}
Was wir oben sehen, ist, dass die Methode InternalStuffInvoker
ein Argument verwendet, das in dieser Funktion überhaupt nicht verwendet wird, sondern nur an die andere private Methode weitergeleitet wird DoInternalStuff
. (Wo DoInternalStuff
wird privat an anderen Orten in dieser Klasse verwendet, z. B. in der DoThatStuff
(öffentlichen) Methode.)
Eine alternative Lösung wäre das Hinzufügen einer Mitgliedsvariablen, die diese Informationen enthält:
class Thingamajig {
private ResultType DoInternalStuff() {
ResultType res;
for (... some loop condition ...) {
...
if (m_calcSelect == typeA) {
...
} ...
}
...
return res;
}
private void InteralStuffInvoker() {
...
DoInternalStuff();
...
}
public void DoThisStuff() {
... some code ...
m_calcSelect = typeA;
InternalStuffInvoker();
... some more code ...
}
public ResultType DoThatStuff() {
... some code ...
m_calcSelect = typeX;
ResultType x = DoInternalStuff();
... some more code ... further process x ...
return x;
}
}
Insbesondere bei tiefen Aufrufketten, bei denen das Selektor-Flag für die innere Methode außerhalb ausgewählt ist, kann die Verwendung einer Elementvariablen die Zwischenfunktionen sauberer machen, da sie keinen Pass-Through-Parameter enthalten müssen.
Andererseits repräsentiert diese Mitgliedsvariable keinen Objektstatus (da sie weder festgelegt noch außerhalb verfügbar ist), sondern ist wirklich ein verstecktes zusätzliches Argument für die "innere" private Methode.
Was sind die Vor- und Nachteile jedes Ansatzes?
quelle
Antworten:
Das verstärkt mein Bauchgefühl beim Lesen Ihrer Frage: Versuchen Sie, diese Daten so lokal wie möglich zu halten, dh fügen Sie sie nicht dem Objektstatus hinzu . Dies reduziert den Statusbereich Ihrer Klasse und erleichtert das Verstehen, Testen und Verwalten Ihres Codes. Ganz zu schweigen davon, dass das Vermeiden eines gemeinsamen Status Ihre Klasse auch threadsicherer macht - dies kann für Sie im Moment ein Problem sein oder auch nicht, aber es kann in Zukunft einen großen Unterschied machen.
Wenn Sie solche Flaggen übermäßig herumreichen müssen, kann dies natürlich zu einem Ärgernis werden. In diesem Fall können Sie in Betracht ziehen
quelle
Ich würde definitiv für die Option "Argument" stimmen - meine Gründe:
Synchronisation - Was ist, wenn die Methode von mehreren Threads gleichzeitig aufgerufen wird? Wenn Sie die Flags als Argument übergeben, müssen Sie den Zugriff auf diese Flags nicht synchronisieren.
Testbarkeit - Stellen Sie sich vor, Sie schreiben einen Komponententest für Ihre Klasse. Wie testen Sie den internen Status des Mitglieds? Was passiert, wenn Ihr Code eine Ausnahme auslöst und das Mitglied in einem unerwarteten Zustand endet?
Trennung von Bedenken - Durch Hinzufügen der Flags zu Ihren Methodenargumenten wird klar, dass keine andere Methode diese verwenden soll. Sie werden es nicht versehentlich in einer anderen Methode verwenden.
quelle
Die erste Alternative scheint mir in Ordnung zu sein. Eine Funktion wird mit einem Parameter aufgerufen und führt etwas aus, das von diesem Parameter abhängt . Selbst wenn Sie den Parameter nur an eine andere Funktion weiterleiten, erwarten Sie, dass das Ergebnis irgendwie vom Parameter abhängt.
Die zweite Alternative besteht darin, eine globale Variable nur innerhalb des Klassenbereichs anstelle des Anwendungsbereichs zu verwenden. Aber es ist das gleiche Problem ... Sie müssen den gesamten Klassencode sorgfältig lesen, um festzustellen, wer und wann diese Werte liest und schreibt.
Daher gewinnt die erste Alternative.
Wenn Sie in der ersten Alternative zu viele Parameter (lesen Sie: zwei oder mehr) verwenden würden, die alle zusammen an die nächste Funktion übergeben werden, können Sie sie in ein Objekt einfügen (machen Sie es zu einer inneren Klasse, wenn es für nicht relevant ist den Rest der Anwendung) und Verwendung dieses Objekts als Parameter.
quelle
Ich möchte "weder" sagen.
Denken Sie zunächst daran, dass Sie bereits wissen, was Sie tun werden, wenn Sie anrufen
DoInternalStuff
. Die Flagge sagt so viel. So , jetzt, statt nur voran gehen und tun es, Sie furzen um eine Funktion aufrufen , dass nun entscheiden muss , was zu tun ist .Wenn Sie der Funktion mitteilen möchten, was zu tun ist, teilen Sie ihr mit, was zu tun ist . Sie können eine tatsächliche Funktion, die Sie für jede Iteration ausführen möchten, als C # -Delegat übergeben (oder als C ++ - Funktionsobjekt oder als Java-ausführbare Datei oder dergleichen).
In C #:
Sie enden immer noch mit dem Streit, der überall herumgeht, aber Sie werden den größten Teil des Wenn / Sonst-Mistes los
DoInternalStuff
. Sie können den Delegaten auch als Feld speichern, wenn Sie möchten.Ob Sie das ToDo-Ding weitergeben oder aufbewahren möchten ... Wenn Sie eines auswählen müssen, geben Sie es weiter. Wenn Sie es aufbewahren, werden Sie wahrscheinlich später in den Arsch gebissen, da Sie die Fadensicherheit so gut wie vernachlässigt haben. Bedenken Sie, dass Sie, wenn Sie beabsichtigen, etwas mit Threads zu tun, jetzt für die gesamte Dauer der Schleife sperren müssen, oder jemand kann die Aufgaben direkt unter Ihnen wechseln. Wenn Sie es bestehen, ist es weniger wahrscheinlich, dass es in den Papierkorb fällt ... und wenn Sie immer noch sperren müssen oder so, können Sie dies für einen Teil einer Iteration anstelle der gesamten Schleife tun.
Ehrlich gesagt ist es gut, dass es nervt. Es gibt wahrscheinlich einen viel einfacheren Weg, um das zu tun, was Sie tun möchten, worüber ich Sie nicht wirklich beraten konnte, da der Code offensichtlich nicht der echte ist. Die beste Antwort variiert je nach Fall.
quelle