it-swarm.com.de

Übergabe der Mitgliedsvariablen als Methodenparameter

In einem Projekt habe ich einen Code wie diesen gefunden:

class SomeClass
{
    private SomeType _someField;

    public SomeType SomeField
    {
        get { return _someField; }
        set { _someField = value; }
    }

    protected virtual void SomeMethod(/*...., */SomeType someVar)
    {
    }

    private void SomeAnotherMethod()
    {
        //.............
        SomeMethod(_someField);
        //.............
    }

};

Wie kann ich meine Teamkollegen davon überzeugen, dass dies ein schlechter Code ist?

Ich glaube, das ist eine unnötige Komplikation. Warum eine Mitgliedsvariable als Methodenparameter übergeben, wenn Sie bereits Zugriff darauf haben? Dies ist auch eine Verletzung der Kapselung.

Sehen Sie weitere Probleme mit diesem Code?

33
tika

Ich denke, dies ist ein gültiges Thema, aber der Grund, warum Sie gemischte Antworten erhalten, liegt in der Art und Weise, wie die Frage gebildet wurde. Persönlich hatte ich die gleichen Erfahrungen mit meinem Team, bei denen es unnötig war, Mitglieder als Argumente zu übergeben, und der Code verschlungen war. Wir hätten eine Klasse, die mit einer Reihe von Mitgliedern arbeitet, aber einige Funktionen greifen direkt auf Mitglieder zu, und andere Funktionen würden dieselben Mitglieder über Parameter ändern (d. H. Völlig andere Namen verwenden), und es gab absolut keinen technischen Grund dafür. Mit technischen Gründen meine ich ein Beispiel, das Kate geliefert hat.

Ich würde empfehlen, einen Schritt zurückzutreten und sich nicht genau auf die Übergabe von Mitgliedern als Parameter zu konzentrieren, sondern mit Ihrem Team Diskussionen über Klarheit und Lesbarkeit einzuleiten. Besprechen Sie entweder formeller oder nur in Fluren, was einige Codesegmente leichter lesbar und andere Codesegmente schwieriger macht. Identifizieren Sie dann Qualitätsmaßstäbe oder Attribute von sauberem Code, die Sie als Team anstreben möchten. Selbst wenn wir an Projekten auf der grünen Wiese arbeiten, verbringen wir mehr als 90% der Zeit mit dem Lesen. Sobald der Code geschrieben ist (sagen wir 10-15 Minuten später), wird er in die Wartung übernommen, wo die Lesbarkeit noch wichtiger ist.

Für Ihr spezielles Beispiel hier würde ich das Argument verwenden, dass weniger Code immer leichter zu lesen ist als mehr Code. Eine Funktion mit 3 Parametern ist für das Gehirn schwieriger zu verarbeiten als eine Funktion mit keinem oder 1 Parameter. Wenn es einen anderen Variablennamen gibt, muss das Gehirn beim Lesen des Codes noch eine andere Sache im Auge behalten. Denken Sie also an "int m_value" und dann an "int localValue" und denken Sie daran, dass das eine wirklich bedeutet, dass das andere für Ihr Gehirn immer teurer ist, als einfach mit "m_value" zu arbeiten.

Für mehr Munition und Ideen würde ich empfehlen, eine Kopie von Onkel Bobs Clean Code zu kaufen.

5
DXM

Ich kann mir eine Rechtfertigung für die Übergabe eines Mitgliedsfelds als Parameter in einer (privaten) Methode vorstellen: Sie macht deutlich, wovon Ihre Methode abhängt.

Wie Sie sagen, sind alle Mitgliedsfelder implizite Parameter Ihrer Methode, da das gesamte Objekt ist. Wird jedoch das vollständige Objekt wirklich benötigt, um das Ergebnis zu berechnen? Wenn SomeMethod eine interne Methode ist, die nur von _someField Abhängt, ist es nicht sauberer, diese Abhängigkeit explizit zu machen? Wenn Sie diese Abhängigkeit explizit angeben, kann dies auch darauf hindeuten, dass Sie diesen Code tatsächlich aus Ihrer Klasse heraus umgestalten können! (Hinweis Ich gehe davon aus, dass wir hier nicht über Getter oder Setter sprechen, sondern über Code, der tatsächlich etwas berechnet.)

Ich würde nicht das gleiche Argument für öffentliche Methoden vorbringen, da der Aufrufer weder weiß noch sich darum kümmert, welcher Teil des Objekts für die Berechnung des Ergebnisses relevant ist ...

31
Andres F.

Ich sehe einen starken Grund für die Übergabe von Mitgliedsvariablen als Funktionsargumente an private Methoden - Funktionsreinheit. Mitgliedsvariablen sind aus Sicht der Sichtweise effektiv ein globaler Zustand, außerdem ein veränderlicher globaler Zustand, wenn sich das Mitglied während der Ausführung der Methode ändert. Durch Ersetzen von Mitgliedsvariablenreferenzen durch Methodenparameter können wir eine Funktion effektiv rein machen. Reine Funktionen hängen nicht von einem externen Zustand ab und haben keine Nebenwirkungen. Bei gleichen Eingabeparametern werden immer dieselben Ergebnisse zurückgegeben. Dies erleichtert das Testen und die zukünftige Wartung.

Sicher, es ist weder einfach noch praktisch, alle Ihre Methoden als reine Methoden in einer OOP Sprache) zu haben. Aber ich glaube, Sie gewinnen viel an Code-Klarheit, wenn Sie die Methoden, die komplexe Logik handhaben, rein und verwendend haben unveränderliche Variablen, während die unreine "globale" Zustandsbehandlung innerhalb dedizierter Methoden getrennt bleibt.

Die Übergabe von Mitgliedsvariablen an eine öffentliche Funktion desselben Objekts beim externen Aufruf der Funktion würde meiner Meinung nach jedoch einen großen Codegeruch darstellen.

30
scrwtp

Wenn die Funktion mehrmals aufgerufen wird, manchmal diese Mitgliedsvariable übergeben und manchmal etwas anderes übergeben wird, ist dies in Ordnung. Zum Beispiel würde ich das überhaupt nicht für schlecht halten:

if ( CalculateCharges(newStartDate) > CalculateCharges(m_StartDate) )
{
     //handle increase in charges
}

dabei ist newStartDate eine lokale Variable und m_StartDate ist eine Mitgliedsvariable.

Wenn die Funktion jedoch immer nur mit der an sie übergebenen Mitgliedsvariablen aufgerufen wird, ist das seltsam. Mitgliedsfunktionen arbeiten ständig an Mitgliedsvariablen. Möglicherweise tun sie dies (abhängig von der Sprache, in der Sie arbeiten), um eine Kopie der Mitgliedsvariablen zu erhalten. Wenn dies der Fall ist und Sie dies nicht sehen können, ist der Code möglicherweise besser, wenn der gesamte Prozess explizit ausgeführt wird.

8
Kate Gregory

Was niemand angesprochen hat, ist, dass SomeMethod virtuell geschützt ist. Dies bedeutet, dass eine abgeleitete Klasse sie verwenden UND ihre Funktionalität erneut implementieren kann. Eine abgeleitete Klasse hätte keinen Zugriff auf die private Variable und könnte daher keine benutzerdefinierte Implementierung von SomeMethod bereitstellen, die von der privaten Variablen abhängt. Anstatt die Abhängigkeit von der privaten Variablen zu übernehmen, muss der Aufrufer sie für die Deklaration übergeben.

2
Michael Brown

Der Hauptgrund für die Verwendung einer Mitgliedsvariablen in einer Klasse besteht darin, dass Sie sie an einer Stelle festlegen und ihren Wert für jede andere Methode in der Klasse verfügbar machen können. Im Allgemeinen würden Sie daher erwarten, dass die Mitgliedsvariable nicht an eine Methode der Klasse übergeben werden muss.

Ich kann mir jedoch einige Gründe vorstellen, warum Sie die Mitgliedsvariable möglicherweise an eine andere Methode der Klasse übergeben möchten. Die erste Möglichkeit besteht darin, sicherzustellen, dass der Wert der Elementvariablen bei Verwendung mit der aufgerufenen Methode unverändert verwendet werden muss, auch wenn diese Methode den tatsächlichen Wert der Elementvariablen zu einem bestimmten Zeitpunkt im Prozess ändern muss. Der zweite Grund hängt mit dem ersten zusammen, da Sie möglicherweise die Unveränderlichkeit eines Werts im Rahmen einer Methodenkette gewährleisten möchten - beispielsweise bei der Implementierung einer fließenden Syntax.

In Anbetracht dessen würde ich nicht so weit gehen zu sagen, dass der Code als solcher "schlecht" ist, wenn Sie eine Mitgliedsvariable an eine der Methoden ihrer Klasse übergeben. Ich würde jedoch vorschlagen, dass dies im Allgemeinen nicht ideal ist, da dies zu einer starken Codeduplizierung führen kann, wenn der Parameter beispielsweise einer lokalen Variablen zugewiesen wird, und die zusätzlichen Parameter dem Code "Rauschen" hinzufügen, wenn er nicht benötigt wird. Wenn Sie ein Fan des Clean Code-Buches sind, wissen Sie, dass darin erwähnt wird, dass Sie die Anzahl der Methodenparameter auf ein Minimum beschränken sollten, und zwar nur dann, wenn es für die Methode keine sinnvollere Möglichkeit gibt, auf den Parameter zuzugreifen .

1
S.Robins

GUI-Frameworks verfügen normalerweise über eine Art 'View'-Klasse, die auf dem Bildschirm gezeichnete Objekte darstellt, und diese Klasse bietet normalerweise eine Methode wie invalidateRect(Rect r), um einen Teil ihres Zeichenbereichs als neu gezeichnet zu markieren. Clients rufen diese Methode möglicherweise auf, um eine Aktualisierung eines Teils der Ansicht anzufordern. Eine Ansicht kann aber auch eine eigene Methode aufrufen, z.

invalidateRect(m_frame);

um eine Neuzeichnung des gesamten Bereichs zu bewirken. Dies kann beispielsweise geschehen, wenn es zum ersten Mal zur Ansichtshierarchie hinzugefügt wird.

Daran ist nichts auszusetzen. Der Rahmen der Ansicht ist ein gültiges Rechteck, und die Ansicht selbst weiß, dass sie sich selbst neu zeichnen möchte. Die View-Klasse könnte eine separate Methode bereitstellen, die keine Parameter akzeptiert und stattdessen den Frame der Ansicht verwendet:

invalidateFrame();

Aber warum nur dafür eine spezielle Methode hinzufügen, wenn Sie die allgemeinere invalidateRect() verwenden können? Wenn Sie sich für die Bereitstellung von invalidateFrame() entschieden haben, würden Sie es höchstwahrscheinlich in Bezug auf die allgemeinere invalidateRect() implementieren:

View::invalidateFrame(void)
{
    invalidateRect(m_frame)
}

Warum Variable als Methodenparameter übergeben, wenn Sie bereits Zugriff darauf haben?

Sie sollten Instanzvariablen als Parameter an Ihre eigene Methode übergeben if Die Methode arbeitet nicht speziell mit dieser Instanzvariablen. Im obigen Beispiel ist der Rahmen der Ansicht für die Methode invalidateRect() nur ein weiteres Rechteck.

1
Caleb

Dies ist sinnvoll, wenn es sich bei der Methode um eine Dienstprogrammmethode handelt. Angenommen, Sie müssen einen eindeutigen Kurznamen aus mehreren Freitextzeichenfolgen ableiten.

Sie möchten nicht für jede Zeichenfolge eine separate Implementierung codieren, sondern die Zeichenfolge an eine gemeinsame Methode übergeben.

Wenn die Methode jedoch immer für ein einzelnes Mitglied funktioniert, erscheint es etwas dumm, sie als Parameter zu übergeben.

1
James Anderson

Einige Dinge, die mir einfallen, wenn ich darüber nachdenke:

  1. Im Allgemeinen sind Methodensignaturen mit weniger Parametern leichter zu verstehen. Ein wichtiger Grund für die Erfindung von Methoden war die Beseitigung langer Parameterlisten, indem sie mit den Daten verknüpft wurden, mit denen sie arbeiten.
  2. Wenn die Methodensignatur von der Mitgliedsvariablen abhängt, wird es in Zukunft schwieriger, diese Variablen zu ändern, da Sie nicht nur innerhalb der Methode, sondern überall dort, wo die Methode aufgerufen wird, Änderungen vornehmen müssen. Und da SomeMethod in Ihrem Beispiel geschützt ist, müssen auch Unterklassen geändert werden.
  3. Methoden (öffentlich oder privat), die nicht von den Interna der Klasse abhängen, müssen nicht zu dieser Klasse gehören. Sie könnten in nützliche Methoden einbezogen werden und genauso glücklich sein. Sie haben so gut wie nichts damit zu tun, Teil dieser Klasse zu sein. Wenn keine andere Methode von dieser Variablen abhängt, nachdem Sie die Methode (n) verschoben haben, sollte diese Variable ebenfalls verwendet werden! Höchstwahrscheinlich sollte sich die Variable auf einem eigenen Objekt befinden, wobei diese Methode oder Methoden, die damit arbeiten, öffentlich werden und von der übergeordneten Klasse zusammengesetzt werden.
  4. Die Weitergabe von Daten an verschiedene Funktionen wie Ihre Klasse ist ein prozedurales Programm, bei dem globale Variablen nur im Widerspruch zu OO design. Dies ist nicht die Absicht von Mitgliedsvariablen und Mitgliedsfunktionen (siehe oben)) stehen. und es klingt so, als ob Ihre Klasse nicht sehr zusammenhängend ist. Ich denke, es ist ein Code-Geruch, der eine bessere Möglichkeit vorschlägt, Ihre Daten und Methoden zu gruppieren.
1
Colin Hunt

Sie fehlen, wenn der Parameter ("Argument") referenziert oder schreibgeschützt ist.

class SomeClass
{
    protected SomeType _someField;
    public SomeType SomeField
    {
        get { return _someField; }

        set {
          if (doSomeValidation(value))
          {
            _someField = value;
          }
        }
    }

    protected virtual void ModifyMethod(/*...., */ ref SomeType someVar)
    { 
      // ...
    }    

    protected virtual void ReadMethod(/*...., */ SomeType someVar)
    { 
      // ...
    }

    private void SomeAnotherMethod()
    {
        //.............

        // not recommended, but, may be required in some cases
        ModifyMethod(ref this._someField);

        //.............

        // recommended, but, verbose
        SomeType SomeVar = this.someField;
        ModifyMethod(ref SomeVar);
        this.someField = SomeVar;

        //.............

        ReadMethod(this.someField);
        //.............
    }

};

Viele Entwickler weisen in den Konstruktormethoden normalerweise direkt die internen Felder einer Variablen zu. Es gibt einige Ausnahmen.

Denken Sie daran, dass "Setter" zusätzliche Methoden haben können, nicht nur Zuweisungen, und manchmal sogar virtuelle Methoden sein oder virtuelle Methoden aufrufen können.

Hinweis: Ich empfehle, die internen Eigenschaftsfelder als "geschützt" zu belassen.

0
umlcat