Ich bin kürzlich auf die folgende Situation gestoßen.
class A{
public:
void calculate(T inputs);
}
Erstens A
stellt es ein Objekt in der physischen Welt dar, was ein starkes Argument dafür ist, die Klasse nicht aufzuteilen. Nun calculate()
stellt sich heraus, dass es eine ziemlich lange und komplizierte Funktion ist. Ich sehe drei mögliche Strukturen dafür:
- Schreiben Sie es als Textwand - Vorteile - alle Informationen sind an einem Ort
- Schreiben Sie
private
Hilfsfunktionen in die Klasse und verwenden Sie sie imcalculate
Hauptteil - Nachteile - der Rest der Klasse kennt / kümmert sich / versteht nicht um diese Methoden schreibe wie
calculate
folgt:void A::calculate(T inputs){ auto lambda1 = () [] {}; auto lambda2 = () [] {}; auto lambda3 = () [] {}; lambda1(inputs.first_logical_chunk); lambda2(inputs.second_logical_chunk); lambda3(inputs.third_logical_chunk); }
Kann dies als eine gute oder schlechte Praxis angesehen werden? Zeigt dieser Ansatz irgendwelche Probleme? Alles in allem sollte ich dies als einen guten Ansatz betrachten, wenn ich wieder mit der gleichen Situation konfrontiert bin?
BEARBEITEN:
class A{
...
public:
// Reconfiguration of the algorithm.
void set_colour(double colour);
void set_density(double density);
void set_predelay(unsigned long microseconds);
void set_reverb_time(double reverb_time, double room_size);
void set_drywet(double left, double right);
void set_room_size(double value);;
private:
// Sub-model objects.
...
}
Alle diese Methoden:
- einen Wert bekommen
- Berechnen Sie einige andere Werte, ohne state zu verwenden
- Rufen Sie einige der "Submodell-Objekte" auf, um ihren Status zu ändern.
Mit Ausnahme set_room_size()
dieser Methoden wird der angeforderte Wert einfach an Unterobjekte übergeben. set_room_size()
Auf der anderen Seite werden einige Bildschirme mit undurchsichtigen Formeln angezeigt, und dann wird (2) ein halber Bildschirm zum Aufrufen von Unterobjektsetzern ausgeführt, um die verschiedenen erhaltenen Ergebnisse anzuwenden. Deshalb habe ich die Funktion in zwei Lambdas aufgeteilt und am Ende der Funktion aufgerufen. Hätte ich es in logischere Stücke aufteilen können, hätte ich mehr Lambdas isoliert.
Unabhängig davon besteht das Ziel der aktuellen Frage darin, festzustellen, ob diese Denkweise fortbestehen soll oder ob sie allenfalls keinen Mehrwert bringt (Lesbarkeit, Wartbarkeit, Debug-Fähigkeit usw.).
Firstly, A represents an object in the physical world, which is a strong argument for not splitting the class up.
SicherlichA
stellt Daten über ein Objekt , das existieren könnte in der physischen Welt. Sie können eine Instanz vonA
ohne das reale Objekt und ein reales Objekt ohne eine Instanz von habenA
, so dass es unsinnig ist, sie so zu behandeln, als wären sie ein und dasselbe.calculate()
Ihnen kennt diese Unterfunktionen.A
sind, ist das ein bisschen extrem.A
stellt es ein Objekt in der physischen Welt dar, was ein starkes Argument dafür ist, die Klasse nicht aufzuteilen." Dies wurde mir leider gesagt, als ich anfing zu programmieren. Es hat Jahre gedauert, bis mir klar wurde, dass es ein Haufen Eishockey ist. Es ist ein schrecklicher Grund, Dinge zu gruppieren. Ich kann nicht artikulieren , was sind gute Gründe für die Gruppe Dinge (zumindest zu meiner Zufriedenheit), aber das ist eine Sie jetzt verwerfen sollte. Das Ende von "gutem Code" ist, dass er richtig funktioniert, relativ leicht zu verstehen und relativ leicht zu ändern ist (dh Änderungen haben keine seltsamen Nebenwirkungen).Antworten:
Nein, dies ist im Allgemeinen kein gutes Muster
Sie zerlegen eine Funktion mit Lambda in kleinere Funktionen. Es gibt jedoch ein viel besseres Werkzeug, um Funktionen aufzubrechen: Funktionen.
Lambdas funktionieren, wie Sie gesehen haben, aber sie bedeuten viel, viel, viel mehr als nur die Aufteilung einer Funktion in lokale Teile . Lambdas machen:
In dem Moment, in dem Sie Lambdas in den Mix aufnehmen, muss der nächste Entwickler, der sich den Code ansieht, alle diese Regeln sofort mental laden, um zu sehen, wie Ihr Code funktioniert. Sie wissen nicht, dass Sie nicht alle diese Funktionen nutzen werden. Dies ist im Vergleich zu den Alternativen sehr teuer.
Es ist wie mit einer Hinterhacke, um Ihre Gartenarbeit zu tun. Sie wissen, dass Sie damit nur kleine Löcher für die diesjährigen Blumen graben, aber die Nachbarn werden nervös.
Bedenken Sie, dass Sie Ihren Quellcode nur visuell gruppieren müssen. Dem Compiler ist es eigentlich egal, dass Sie Dinge mit Lambdas belegen. Tatsächlich würde ich erwarten, dass der Optimierer alles, was Sie gerade beim Kompilieren getan haben, sofort rückgängig macht. Sie sind nur für den nächsten Leser da (Vielen Dank, auch wenn wir uns in Bezug auf die Methodik nicht einig sind! Code wird weitaus häufiger gelesen als geschrieben!). Alles, was Sie tun, ist die Gruppierungsfunktionalität.
// ------------------
zwischen den einzelnen Teilen.BEARBEITEN: Nachdem ich Ihre Bearbeitung mit Beispielcode gesehen habe, neige ich dazu, dass die Kommentarnotation die sauberste ist und Klammern verwendet werden, um die in den Kommentaren angegebenen Grenzen durchzusetzen. Wenn jedoch eine der Funktionen in anderen Funktionen wiederverwendet werden kann, würde ich empfehlen, stattdessen Funktionen zu verwenden
quelle
count++
)calculate()
in{}
Blöcke aufgeteilt und gemeinsam genutzte Daten imcalculate()
Geltungsbereich deklariert werden . Ich dachte, wenn ich sehe, dass die Lambdas nicht fangen, würde ein Leser nicht durch die Macht der Lambdas belastet.lambda
unfair ist oder ob Sie die Leute grob dazu zwingen, der strengen Konnotation zu folgen, ist keine leicht zu beantwortende Frage. In der Tat kann es völlig akzeptabel sein, dass Sie inlambda
Ihrem Unternehmen so vorgehen, und in meinem Unternehmen völlig inakzeptabel, und keiner von beiden muss sich wirklich irren!lambda
, wie derfor_each
Funktion, verletzt wurde . Wenn ich also einen Fehler sehelambda
, der nicht zu einem dieser leicht auffindbaren Problemfälle passt, gehe ich zunächst davon aus, dass er wahrscheinlich für die funktionale Programmierung verwendet wird, da er ansonsten nicht benötigt wird. Für viele Entwickler ist funktionale Programmierung eine völlig andere Denkweise als prozedurale oder OO-Programmierung.Ich denke, Sie haben eine schlechte Annahme gemacht:
Ich bin damit nicht einverstanden. Wenn ich zum Beispiel eine Klasse hätte, die ein Auto repräsentiert, würde ich sie definitiv aufteilen wollen, weil ich sicher möchte, dass eine kleinere Klasse die Reifen repräsentiert.
Sie sollten diese Funktion in kleinere private Funktionen aufteilen. Wenn es wirklich vom anderen Teil der Klasse getrennt zu sein scheint, kann dies ein Zeichen dafür sein, dass die Klasse getrennt werden sollte. Natürlich ist es schwer zu sagen, ohne ein explizites Beispiel.
In diesem Fall sehe ich den Vorteil der Verwendung von Lambda-Funktionen nicht wirklich, da der Code dadurch nicht wirklich sauberer wird. Sie wurden entwickelt, um die Programmierung funktionaler Stile zu unterstützen, aber das ist nicht das Gegenteil.
Was Sie geschrieben haben, ähnelt ein wenig geschachtelten Funktionsobjekten im Javascript-Stil. Was wiederum ein Zeichen dafür ist, dass sie eng zusammengehören. Sind Sie sicher, dass Sie keine separate Klasse für sie machen sollten?
Zusammenfassend denke ich nicht, dass dies ein gutes Muster ist.
AKTUALISIEREN
Wenn Sie keine Möglichkeit sehen, diese Funktionalität in einer aussagekräftigen Klasse zu kapseln, können Sie dateibezogene Hilfsfunktionen erstellen, die nicht zu Ihrer Klasse gehören. Dies ist schließlich C ++, OO-Design ist kein Muss.
quelle
A
(vielleicht sogar functor mit allen anderen Methoden private)? Klasse deklariert und definiertcalculate()
(dies sieht meinem Lambda-Beispiel sehr ähnlich). Zur Verdeutlichung:calculate()
Gehört zu einer Familie von Methoden (calculate_1(), calculate_2()
usw.), von denen alle einfach sind, ist nur diese eine 2-Bildschirme-Formeln.calculate()
so viel länger als bei allen anderen Methoden?