Angenommen, ich habe eine Reihe von Läufern, mit denen ich den größten Läufer, den schnellsten Läufer und den leichtesten Läufer finden muss. Die am besten lesbare Lösung scheint zu sein:
runners = getRunners();
tallestRunner = getTallestRunner(runners);
fastestRunner = getFastestRunner(runners);
lightestRunner = getLightestRunner(runners);
..wobei jede Funktion über die Läufer iteriert und die größte Höhe, die größte Geschwindigkeit und das niedrigste Gewicht verfolgt. Dreimal über das Array zu iterieren, scheint jedoch keine sehr gute Idee zu sein. Es wäre stattdessen besser zu tun:
int greatestHeght, greatestSpeed, leastWeight;
Runner tallestRunner, fastestRunner, lightestRunner;
for(runner in runners){
if(runner.height > greatestHeight) { greatestHeight = runner.height; tallestRunner = runner; }
if(runner.speed > ...
}
Dies ist zwar nicht zu unlesbar, kann jedoch unübersichtlich werden, wenn für jede in der Iteration extrahierte Information mehr Logik vorhanden ist.
Was ist der Mittelweg hier? Wie kann ich nur eine einzige Iteration verwenden, während der Code in logische Einheiten unterteilt bleibt?
design
code-quality
language-agnostic
readability
mowwwalker
quelle
quelle
Antworten:
Taskinoor hat die richtige Idee, aber es gibt eine bessere Möglichkeit, sie umzusetzen ... vorausgesetzt, Ihre Sprache unterstützt die Weitergabe einer Funktionsreferenz.
Hier ist zum Beispiel, wie ich es im C # -ischen Stil machen würde:
Das Erweitern ist trivial. Anstatt drei Funktionen zu definieren, können Sie ein Array
Func<Runner, Runner, Runner>
anonymer Funktionen definieren und einfach alle ausführen. Sie können dies sogar mit regulären Funktionen wie tunRunner pickTallest(Runner x, Runner y)
, obwohl Sie diese dann explizit definieren müssen. Der Schlüssel ist jedoch, dass Sie nicht den Wert für jede Statistik verfolgen müssen - Sie müssen nur wissen, wie man zwei vergleichtRunners
und diejenige mit dem besseren Wert auswählt.quelle
Dies ist eines der Dinge, bei denen Sie häufig einen Datenblock bearbeiten möchten und nicht das OO-Prinzip "eines einzelnen Datenelements".
Also würde ich die gesamte Liste in eine Klasse einschließen, die bei der Erstellung die Liste analysiert und berechnet, was Sie wollen. Sie können diese Klasse auch zum Einfügen und Entfernen aus der Liste verwenden, damit die umschlossenen Informationen immer auf dem neuesten Stand sind.
Das ist es. Sie haben jetzt einen in sich geschlossenen Logikblock, der Ihre Fragen mit nur einer einzigen Iteration bei der Erstellung und beim Entfernen von Objekten beantwortet.
quelle
Sie können alle drei Werte auf einmal zurückgeben. Im Pseudocode in der Nähe von Scala:
Das gibt Ihnen ein Tupel der Läufer, die Sie suchen. Sie können dasselbe in anderen Sprachen tun. Möglicherweise müssen Sie eine Bibliothek wie Guava oder Underscore herunterladen, um dies zu tun, und Sie müssen das Tupel möglicherweise in ein Objekt einschließen.
quelle
Erstellen Sie eine RunnerStats-Klasse, die die Statistiken in einem einzigen berechnet
for loop
, genau wie in Ihrem zweiten Code-Snippet.Dann lesen Sie die Statistiken in die Variablen über
getters
. Die Getter geben nur die bereits berechneten Werte zurück, sie berechnen nichts.Auf diese Weise erhalten Sie das Beste aus beiden Lösungen: Effizienz und Lesbarkeit.
quelle
Das Problem, das Sie beschreiben, ist sehr häufig: Sie haben eine Schleife, die bestimmte Daten erzeugt, und Sie möchten mehrere "Statistiken" aus den Gesamtdaten aggregieren. Die einfache Implementierung besteht, wie Sie sagten, darin, mehrere lokale Variablen zu haben, die jeweils in jeder Iteration aktualisiert werden:
Dies hat keine gute Trennung der Bedenken, da die Aggregationslogik mit der Datenproduktion übereinstimmt. Das Extrahieren der Aggregationslogik in eine Methode (wie in dieser Antwort vorgeschlagen ) ist eine Verbesserung, aber die Isolation ist immer noch nicht gut: Die Zwischenergebnisse und (falls erforderlich) Hilfsvariablen wie müssen
greatestHeight
immer noch lokale Variablen sein.IMHO ist die einzig gute Lösung, sowohl die Aggregationslogik als auch die Zuweisungen in eine Methode zu extrahieren .
Wie kann das gemacht werden? Indem Sie zuerst die lokalen Variablen in Felder umgestalten. Dann können Sie zB eine Methode extrahieren,
updateStats(runner)
die die FeldertallestRunner
/fastestRunner
/ ... und die entsprechenden FeldergreatestHeight
/greatestSpeed
/ ... aktualisiert .Aber macht das die Isolation nicht noch schlimmer? Ja, zunächst, aber dies kann durch ein Refactoring der Extraktklasse behoben werden : Verschieben Sie die Statistikfelder und die
updateStats
Methode in eine neue (z. B. verschachtelte) Klasse. Am Ende haben Sie also die folgende lesbare Single-Pass-Lösung:quelle