Langes Methoden-Refactoring: Belassen, wie es ist, in Methoden trennen oder lokale Funktionen verwenden

9

Angenommen, ich habe eine lange Methode wie diese:

public void SomeLongMethod()
{
    // Some task #1
    ...

    // Some task #2
    ...
}

Diese Methode enthält keine sich wiederholenden Teile, die in eine separate Methode oder lokale Funktion verschoben werden sollten.

Es gibt viele Leute (einschließlich mir), die denken, dass lange Methoden Code-Gerüche sind. Ich mag auch keine Idee, hier #region(s) zu verwenden, und es gibt eine sehr beliebte Antwort, die erklärt, warum dies schlecht ist .


Aber wenn ich diesen Code in Methoden aufteile

public void SomeLongMethod()
{
    Task1();

    Task2();
}

private void Task1()
{
    // Some task #1
    ...
}

private void Task2()
{
    // Some task #1
    ...
}

Ich sehe die folgenden Probleme:

  • Verschmutzenden Klassendefinition Umfang mit Definitionen , die intern von einem einzigen Verfahren verwendet werden und was bedeuten soll ich irgendwo dokumentieren , dass Task1und Task2sind nur für den Interna von gedacht SomeLongMethod(oder jede Person , die mein Code liest muss diese Idee ableiten).

  • Verschmutzende IDE-Autovervollständigung (z. B. Intellisense) von Methoden, die innerhalb der einzelnen SomeLongMethodMethode nur einmal verwendet werden.


Wenn ich diesen Methodencode in lokale Funktionen aufteile

public void SomeLongMethod()
{
    Task1();

    Task2();

    void Task1()
    {
        // Some task #1
        ...
    }

    void Task2()
    {
        // Some task #1
        ...
    }
}

dann hat dies nicht die Nachteile separater Methoden, aber dies sieht (zumindest für mich) nicht besser aus als die ursprüngliche Methode.


Welche Version von SomeLongMethodist für Sie wartbarer und lesbarer und warum?

Vadim Ovchinnikov
quelle
@gnat meine Frage ist spezifisch für c # , nicht für Java . Mein Hauptanliegen ist die klassische Methode gegenüber der lokalen Funktion, nicht nur die Trennung in Methoden oder nicht.
Vadim Ovchinnikov
@gnat Auch AFAIK Java (im Gegensatz zu C #) unterstützt keine verschachtelten Funktionen.
Vadim Ovchinnikov
1
Das private Schlüsselwort schützt sicherlich Ihren 'Klassendefinitionsbereich'?
Ewan
1
warte ...... wie groß ist deine Klasse?
Ewan

Antworten:

13

In sich geschlossene Aufgaben sollten in eigenständige Methoden verschoben werden. Es gibt keinen Nachteil, der groß genug ist, um dieses Ziel zu überschreiben.

Sie sagen, dass sie den Namespace der Klasse verschmutzen, aber das ist nicht der Kompromiss, den Sie beim Faktorisieren Ihres Codes berücksichtigen müssen. Ein zukünftiger Leser der Klasse (z. B. Sie) fragt viel häufiger: "Was macht diese spezielle Methode und wie muss ich sie ändern, damit sie das tut, was die geänderten Anforderungen aussagen?" als "Was ist der Zweck und das Gefühl, alle Methoden in der Klasse zu sein? " Daher ist es viel wichtiger, jede der Methoden verständlicher zu machen (indem sie weniger Elemente enthält), als die gesamte Klasse verständlicher zu machen (indem es weniger Methoden hat).

Wenn die Aufgaben nicht wirklich in sich geschlossen sind, sondern einige Statusvariablen erfordern, um sich zu bewegen, kann stattdessen ein Methodenobjekt, ein Hilfsobjekt oder ein ähnliches Konstrukt die richtige Wahl sein. Und wenn die Aufgaben völlig eigenständig sind und überhaupt nicht an die Anwendungsdomäne gebunden sind (z. B. nur die Formatierung von Zeichenfolgen, sollten sie möglicherweise in eine ganz andere (Dienstprogramm-) Klasse fallen. Aber das ändert nichts an der Tatsache, dass " Methoden pro Klasse niedrig zu halten "ist bestenfalls ein sehr untergeordnetes Ziel.

Kilian Foth
quelle
Sie sind also für Methoden, nicht für lokale Funktionen, ja?
Vadim Ovchinnikov
2
@VadimOvchinnikov Mit einer modernen Code-Folding-IDE gibt es kaum einen Unterschied. Der größte Vorteil einer lokalen Funktion besteht darin, dass sie auf Variablen zugreifen kann, die Sie sonst als Parameter weitergeben müssten. Wenn es jedoch viele davon gibt, waren die Aufgaben zunächst nicht wirklich unabhängig.
Kilian Foth
6

Ich mag keinen Ansatz. Sie haben keinen breiteren Kontext angegeben, aber meistens sieht es so aus:

Sie haben eine Klasse, die einige Aufgaben erledigt, indem sie die Leistung mehrerer Dienste in einem einzigen Ergebnis kombiniert.

class SomeClass
{
    private readonly Service1 _service1;
    private readonly Service2 _service2;

    public void SomeLongMethod()
    {
        _service1.Task1();
        _service2.Task2();       
    }
}

Jeder Ihrer Dienste implementiert nur einen Teil der Logik. Etwas Besonderes, das nur dieser Service leisten kann. Wenn es andere private Methoden als nur solche gibt, die die Haupt-API unterstützen, so dass Sie sie einfach sortieren können und es sowieso nicht zu viele davon geben sollte.

class Service1
{
    public void Task1()
    {
        // Some task #1
        ...
    }

    private void SomeHelperMethod() {}
}

class Service2
{
    void Task2()
    {
        // Some task #1
        ...
    }
}

Das Fazit lautet: Wenn Sie Regionen in Ihrem Code erstellen, um Methoden zu gruppieren, ist es an der Zeit, über die Kapselung ihrer Logik mit einem neuen Dienst nachzudenken.

t3chb0t
quelle
2

Das Problem beim Erstellen von Funktionen innerhalb der Methode besteht darin, dass die Länge der Methode nicht verringert wird.

Wenn Sie die zusätzliche Schutzstufe "Private to Method" wünschen, können Sie eine neue Klasse erstellen

public class BigClass
{
    public void SomeLongMethod();

    private class SomeLongMethodRunner
    {
        private void Task1();
        private void Task2();
    }
}

Aber Klassen in Klassen sind sehr hässlich: /

Ewan
quelle
2

Es wird empfohlen, den Umfang von Sprachkonstrukten wie Variablen und Methoden zu minimieren. Vermeiden Sie beispielsweise globale Variablen. Dies erleichtert das Verständnis des Codes und hilft, Missbrauch zu vermeiden, da auf das Konstrukt an weniger Stellen zugegriffen werden kann.

Dies spricht für die Verwendung der lokalen Funktionen.

fsl
quelle
1
hmm sicherlich wiederverwenden Code == eine Methode unter vielen Anrufern teilen. dh Maximierung seines Umfangs
Ewan
1

Ich würde zustimmen, dass lange Methoden oft ein Codegeruch sind, aber ich denke nicht, dass das das ganze Bild ist, sondern dass die Methode lang ist, was auf ein Problem hinweist. Meine allgemeine Regel lautet: Wenn der Code mehrere unterschiedliche Aufgaben ausführt, sollte jede dieser Aufgaben eine eigene Methode sein. Für mich ist es wichtig, ob sich diese Codeabschnitte wiederholen oder nicht. es sei denn, Sie sind sich wirklich absolut sicher, dass niemand diese in Zukunft einzeln einzeln aufrufen möchte (und das ist sehr schwer zu sagen!), setzen Sie sie in eine andere Methode (und machen Sie sie privat - was ein sehr starker Indikator sein sollte Sie erwarten nicht, dass es außerhalb der Klasse verwendet wird.

Ich muss gestehen, dass ich noch keine lokalen Funktionen verwendet habe, daher ist mein Verständnis hier noch lange nicht vollständig, aber der von Ihnen angegebene Link legt nahe, dass sie häufig ähnlich wie ein Lambda-Ausdruck verwendet werden (obwohl es sich hier um eine Reihe von Anwendungsfällen handelt wo sie möglicherweise geeigneter sind als Lambdas oder wo Sie kein Lambda verwenden können, siehe Lokale Funktionen im Vergleich zu Lambda-Ausdrücken für Einzelheiten). Hier könnte es also an der Granularität der "anderen" Aufgaben liegen; Wenn sie ziemlich trivial sind, wäre dann vielleicht eine lokale Funktion in Ordnung? Andererseits habe ich Beispiele für gigantische Lambda-Ausdrücke gesehen (wahrscheinlich, bei denen ein Entwickler kürzlich LINQ entdeckt hat), die den Code weitaus weniger verständlich und wartbar gemacht haben, als wenn er einfach von einer anderen Methode aufgerufen worden wäre.

Auf der Seite Lokale Funktionen heißt es:

'Lokale Funktionen machen die Absicht Ihres Codes klar. Jeder, der Ihren Code liest, kann sehen, dass die Methode nur mit der enthaltenen Methode aufgerufen werden kann. Bei Teamprojekten machen sie es einem anderen Entwickler auch unmöglich, die Methode fälschlicherweise direkt von einer anderen Stelle in der Klasse oder im Aufbau aufzurufen. '

Ich bin mir nicht sicher, ob ich wirklich mit MS als Grund für die Verwendung bin. Der Umfang Ihrer Methode (zusammen mit ihrem Namen und ihren Kommentaren) sollte klar machen, was Ihre Absicht zu diesem Zeitpunkt war . Mit einer lokalen Funktion konnte ich sehen, dass andere Entwickler dies als sakrosankter ansehen, aber möglicherweise bis zu dem Punkt, an dem sie, wenn in Zukunft eine Änderung eintritt, die die Wiederverwendung dieser Funktionalität erfordert, ihre eigene Prozedur schreiben und die lokale Funktion beibehalten ;; Dadurch wird ein Problem mit der Codeduplizierung erstellt. Der letztere Satz aus dem obigen Zitat könnte relevant sein, wenn Sie den Teammitgliedern nicht vertrauen, dass sie eine private Methode verstehen, bevor Sie sie aufrufen, und sie daher unangemessen aufrufen (Ihre Entscheidung könnte davon betroffen sein, obwohl Bildung hier eine bessere Option wäre). .

Zusammenfassend würde ich es im Allgemeinen vorziehen, in Methoden aufzuteilen, aber MS hat aus einem bestimmten Grund lokale Funktionen geschrieben, daher würde ich sicherlich nicht sagen, dass sie nicht verwendet werden.

d219
quelle