it-swarm.com.de

Zusätzliche Zeile im Block vs zusätzlicher Parameter im Clean Code

Kontext

In Clean Code , Seite 35, heißt es

Dies bedeutet, dass die Blöcke in if-Anweisungen, else-Anweisungen, while-Anweisungen usw. eine Zeile lang sein sollten. Wahrscheinlich sollte diese Zeile ein Funktionsaufruf sein. Dies hält nicht nur die umschließende Funktion klein, sondern erhöht auch den Dokumentationswert, da die im Block aufgerufene Funktion einen gut beschreibenden Namen haben kann.

Ich stimme vollkommen zu, das macht sehr viel Sinn.

Später, auf Seite 40, werden Funktionsargumente beschrieben

Die ideale Anzahl von Argumenten für eine Funktion ist Null (niladisch). Als nächstes kommt eins (monadisch), dicht gefolgt von zwei (dyadisch). Drei Argumente (triadisch) sollten nach Möglichkeit vermieden werden. Mehr als drei (polyadisch) erfordern eine ganz besondere Begründung - und sollten dann sowieso nicht verwendet werden. Argumente sind schwer. Sie brauchen viel konzeptionelle Kraft.

Ich stimme vollkommen zu, das macht sehr viel Sinn.

Problem

Ziemlich oft erstelle ich jedoch eine Liste aus einer anderen Liste und muss mit einem von zwei Übeln leben.

Entweder I benutze zwei Zeilen im Block, eine zum Erstellen des Dings, eine zum Hinzufügen zum Ergebnis:

    public List<Flurp> CreateFlurps(List<BadaBoom> badaBooms)
    {
        List<Flurp> flurps = new List<Flurp>();
        foreach (BadaBoom badaBoom in badaBooms)
        {
            Flurp flurp = CreateFlurp(badaBoom);
            flurps.Add(flurp);
        }
        return flurps;
    }

Oder ich füge der Funktion ein Argument hinz für die Liste, zu der das Ding hinzugefügt wird, was es "ein Argument schlimmer" macht.

    public List<Flurp> CreateFlurps(List<BadaBoom> badaBooms)
    {
        List<Flurp> flurps = new List<Flurp>();
        foreach (BadaBoom badaBoom in badaBooms)
        {
            CreateFlurpInList(badaBoom, flurps);
        }
        return flurps;
    }

Frage

Gibt es (Nachteile), die ich nicht sehe und die einen davon generell vorzuziehen machen? Oder gibt es solche Vorteile in bestimmten Situationen; Worauf sollte ich in diesem Fall achten, wenn ich eine Entscheidung treffe?

33
R. Schmitz

Diese Richtlinien sind ein Kompass, keine Karte. Sie weisen dich in eine vernünftige Richtung. Aber sie können Ihnen nicht absolut sagen, welche Lösung „am besten“ ist. Irgendwann müssen Sie aufhören, in die Richtung zu gehen, in die Ihr Kompass zeigt, weil Sie an Ihrem Ziel angekommen sind.

Clean Code ermutigt Sie, Ihren Code in sehr kleine, offensichtliche Blöcke zu unterteilen. Das ist eine allgemein gute Richtung. Aber wenn Sie auf das Äußerste gebracht werden (wie eine wörtliche Interpretation des zitierten Ratschlags nahelegt), haben Sie Ihren Code in nutzlos kleine Teile unterteilt. Nichts macht wirklich etwas, alles nur Delegierte. Dies ist im Wesentlichen eine andere Art der Codeverschleierung.

Es ist Ihre Aufgabe, „kleiner ist besser“ gegen „zu klein ist nutzlos“ abzuwägen. Fragen Sie sich, welche Lösung einfacher ist. Für mich ist das eindeutig die erste Lösung, da offensichtlich eine Liste zusammengestellt wird. Dies ist eine gut verstandene Redewendung. Es ist möglich, diesen Code zu verstehen, ohne sich eine weitere Funktion ansehen zu müssen.

Wenn es besser geht, stellen Sie fest, dass „Alle Elemente von einer Liste in eine andere Liste umwandeln“ ein allgemeines Muster ist, das häufig mithilfe einer funktionalen Operation map() abstrahiert werden kann. In C # heißt es wohl Select. Etwas wie das:

public List<Flurp> CreateFlurps(List<BadaBoom> badaBooms)
{
    return badaBooms.Select(BadaBoom => CreateFlurp(badaBoom)).ToList();
}
104
amon

Die ideale Anzahl von Argumenten für eine Funktion ist Null (niladisch).

Nein! Die ideale Anzahl von Argumenten für eine Funktion ist eins. Wenn es Null ist, garantieren Sie, dass die Funktion auf externe Informationen zugreifen muss, um eine Aktion ausführen zu können. "Onkel" Bob hat das sehr falsch verstanden.

In Bezug auf Ihren Code enthält Ihr erstes Beispiel nur zwei Zeilen im Block, da Sie in der ersten Zeile eine lokale Variable erstellen. Entfernen Sie diese Zuordnung, und Sie halten diese Richtlinien für sauberen Code ein:

public List<Flurp> CreateFlurps(List<BadaBoom> badaBooms)
{
    List<Flurp> flurps = new List<Flurp>();
    foreach (BadaBoom badaBoom in badaBooms)
    {
        flurps.Add(CreateFlurp(badaBoom));
    }
    return flurps;
}

Aber das ist sehr langwieriger (C #) Code. Mach es einfach als:

IEnumerable<Flurp> CreateFlurps(IEnumerable<BadaBoom> badaBooms) =>
    from badaBoom in babaBooms select CreateFlurp(badaBoom);
61
David Arno

Der "Clean Code" -Ratschlag ist völlig falsch.

Verwenden Sie zwei oder mehr Zeilen in Ihrer Schleife. Das Ausblenden derselben zwei Zeilen in einer Funktion ist sinnvoll, wenn es sich um zufällige Mathematik handelt, für die eine Beschreibung erforderlich ist, aber nichts, wenn die Zeilen bereits beschreibend sind. 'Erstellen' und 'Hinzufügen'

Die zweite Methode, die Sie in erwähnen, macht keinen Sinn, da Sie nicht gezwungen sind, ein zweites Argument hinzuzufügen, um die beiden Zeilen zu vermeiden.

public List<Flurp> CreateFlurps(List<BadaBoom> badaBooms)
    {
        List<Flurp> flurps = new List<Flurp>();
        foreach (BadaBoom badaBoom in badaBooms)
        {
            flurps.Add(badaBoom .CreateFlurp());
            //or
            badaBoom.AddToListAsFlurp(flurps);
            //or
            flurps.Add(new Flurp(badaBoom));
            //or
            //make flurps a member of the class
            //use linq.Select()
            //etc
        }
        return flurps;
    }

oder

foreach(var flurp in ConvertToFlurps(badaBooms))...

Wie von anderen angemerkt, ist der Rat, dass die beste Funktion eine ohne Argumente ist, verzerrt auf OOP im besten Fall und einfach schlechter Rat im schlimmsten Fall)

19
Ewan

Zweitens ist es definitiv schlimmer, da CreateFlurpInList die Liste akzeptiert und diese Liste ändert, wodurch die Funktion nicht rein und schwerer zu überlegen ist. Nichts im Methodennamen deutet darauf hin, dass die Methode nur zur Liste hinzugefügt wird.

Und ich biete die dritte, beste Option an:

public List<Flurp> CreateFlurps(List<BadaBoom> badaBooms)
{
    return badaBooms.Select(CreateFlurp).ToList();
}

Und zum Teufel, Sie können diese Methode sofort einbinden, wenn es nur einen Ort gibt, an dem sie verwendet wird, da der Einzeiler von selbst klar ist und daher nicht durch eine Methode gekapselt werden muss, um ihm eine Bedeutung zu geben.

15
Euphoric

Die Version mit einem Argument ist besser, aber nicht in erster Linie wegen der Anzahl der Argumente.

Der wichtigste Grund, warum es besser ist, ist, dass es niedrigere Kopplung hat, was es nützlicher, einfacher zu überlegen, einfacher zu testen und weniger wahrscheinlich zu kopierten + eingefügten Klonen macht.

Wenn Sie mir eine CreateFlurp(BadaBoom) zur Verfügung stellen, kann ich diese mit jeder Art von Sammelcontainer verwenden: Einfach Flurp[], List<Flurp>, LinkedList<Flurp>, Dictionary<Key, Flurp> Und so weiter. Aber mit einer CreateFlurpInList(BadaBoom, List<Flurp>) komme ich morgen zu Ihnen zurück und frage nach CreateFlurpInBindingList(BadaBoom, BindingList<Flurp>), damit mein Ansichtsmodell die Benachrichtigung erhält, dass sich die Liste geändert hat. Yuck!

Als zusätzlichen Vorteil passt die einfachere Signatur eher zu vorhandenen APIs. Sie sagen, Sie haben ein wiederkehrendes Problem

ziemlich oft erstelle ich eine Liste aus einer anderen Liste

Es ist nur eine Frage der Verwendung der verfügbaren Tools. Die kürzeste, effizienteste und beste Version ist:

var Flurps = badaBooms.ConvertAll(CreateFlurp);

Dies ist nicht nur weniger Code zum Schreiben und Testen, sondern auch schneller, da List<T>.ConvertAll() intelligent genug ist, um zu wissen, dass das Ergebnis die gleiche Anzahl von Elementen wie die Eingabe enthält, und die Ergebnisliste vorab zuzuweisen die richtige Größe. Während Ihr Code (beide Versionen) das Erweitern der Liste erforderte.

10
Ben Voigt

Beachten Sie das allgemeine Ziel: den Code einfach zu lesen und zu warten.

Oft ist es möglich, mehrere Zeilen in einer einzigen sinnvollen Funktion zu gruppieren. Tun Sie dies in diesen Fällen. Gelegentlich müssen Sie Ihren allgemeinen Ansatz überdenken.

In Ihrem Fall ersetzen Sie beispielsweise die gesamte Implementierung durch var

flups = badaBooms.Select(bb => new Flurp(bb));

könnte eine Möglichkeit sein. Oder du machst so etwas

flups.Add(new Flurp(badaBoom))

Manchmal passt die sauberste und am besten lesbare Lösung einfach nicht in eine Zeile. Sie haben also zwei Zeilen. Machen Sie den Code nicht schwerer verständlich, nur um eine beliebige Regel zu erfüllen.

Ihr zweites Beispiel ist (meiner Meinung nach) wesentlich schwerer zu verstehen als das erste. Sie haben nicht nur einen zweiten Parameter, sondern der Parameter wird von der Funktion geändert. Schauen Sie nach, was Clean Code dazu zu sagen hat. (Ich habe das Buch momentan nicht zur Hand, aber ich bin mir ziemlich sicher, dass es im Grunde genommen "Mach das nicht, wenn du es vermeiden kannst").

6
doubleYou