Wie vermeide ich Riesenleimmethoden?

21

In meinem aktuellen Job wurde ich einige Male damit beauftragt, alten Code zu bereinigen. Oft ist der Code ein Labyrinth und die Daten dahinter sind noch verworrener. Ich finde mich dabei, Dinge in nette, saubere, modulare Methoden zu zerlegen. Jede Methode macht eine Sache und macht es gut. Dann geht es in den Süden ...

Ausnahmslos habe ich eine saubere API und keine wirkliche Möglichkeit, alles zusammenzubinden. Die Lösung bestand darin, eine große hässliche "Kleber" -Methode (im Allgemeinen voll von bedingten Anweisungen) zu schreiben, die schließlich alle meine "sauberen" Methoden aufruft.

Die Klebemethode ist in der Regel eine knappe Version des Code- / Datengewirrs, das ich bereinigen wollte. Es ist in der Regel besser lesbar, aber es ist immer noch ärgerlich.

Wie kann ich solche Methoden vermeiden? Ist dies ein Symptom für die verwickelten Daten oder ein Spiegelbild von etwas, das ich falsch mache?

cmhobbs
quelle
3
APIs sollen verwendet werden. Aus dem Wirrwarr, das Sie bekommen haben, haben Sie eine API erstellt und sie dann erneut verwirrt. Vielleicht ist es nur die Geschäftsanforderung. Sie haben jedoch einen Mehrwert geschaffen, da andere Benutzer mit Ihrer API problemlos eine andere Klebefunktion ausführen können. Keine Notwendigkeit, die Hände zu wringen ...
Aditya MP
1
Sprechen wir hier überhaupt über Objekte oder nur über Funk?
Erik Reppen
3
Ich denke nicht, dass dies ein Duplikat dieser Frage ist, ich spreche etwas allgemeiner (und in größerem Maßstab als eine einzelne Funktion).
cmhobbs
1
erik - ich spreche hier über objekte und methoden. Ich habe ein paar bedingte Probleme gelöst und daraus APIs gemacht. Das Problem tritt auf, wenn die API aufgerufen werden muss. Die erste Antwort hier könnte genau das sein, wonach ich suche.
cmhobbs
2
Wie um alles in der Welt ist das ein Duplikat?
MattDavey

Antworten:

12

Ich werde Ihnen unsere Erfahrung beim Refactoring von LedgerSMB geben. Wir haben uns schon früh dafür entschieden, die Dinge anders zu machen und tun immer noch genau das, was Sie beschreiben, aber ohne viele Klebemethoden (wir haben übrigens ein paar Klebemethoden, nur nicht viele).

Leben mit zwei Codebasen

LedgerSMB hat ungefähr 5 Jahre mit zwei Codebasen überlebt, und es wird noch einige Jahre dauern, bis die alte Codebasis beseitigt ist. Die alte Codebasis ist ein wahrer Horror. Bei schlechtem db-Design konstruiert Perl wie IS->some_func(\%$some_object);Code, der genau zeigt, warum die Spaghetti-Metapher manchmal verwendet wird (Ausführungspfade, die sich zwischen Modulen und zurück und zwischen Sprachen ohne Reim oder Grund bewegen). Die neue Codebasis vermeidet dies, indem Datenbankabfragen in gespeicherte Prozeduren verschoben werden, ein saubereres Framework für die Verarbeitung von Anforderungen zur Verfügung steht und vieles mehr.

Das erste, was wir uns vorgenommen haben, war zu versuchen, Modul für Modul umzugestalten. Dies bedeutet, alle Funktionen in einem bestimmten Bereich in ein neues Modul zu verschieben und dann den alten Code in das neue Modul einzufügen. Wenn die neue API sauber ist, ist dies keine große Sache. Wenn die neue API nicht funktioniert, werden die Dinge unruhig und das ist eine Einladung, ein bisschen härter an der neuen API zu arbeiten.

Die zweite Sache ist, dass es viele Male gibt, in denen neuer Code auf Logik in altem Code zugreifen muss. Dies ist so weit wie möglich zu vermeiden, da es zu hässlichen Klebemethoden führt, die man aber nicht immer vermeiden kann. In diesem Fall sollten die Klebemethoden so weit wie möglich minimiert und vermieden werden, aber bei Bedarf eingesetzt werden.

Damit dies funktioniert, müssen Sie alle Funktionen in einem bestimmten Bereich neu schreiben . Wenn Sie beispielsweise den gesamten Kundeninformations-Tracking-Code auf einmal umschreiben können, bedeutet dies, dass der Code, der dies vom alten Code aufruft, nicht schwer zu verarbeiten ist und der Versand an den alten Code vom neuen Code minimiert wird.

Die zweite Sache ist, dass Sie, wenn Sie vernünftige Abstraktionen an Ihrer Stelle haben, in der Lage sein sollten, zu wählen, welche Ebene der API aufgerufen werden soll und wie diese sauber gehalten werden soll. Sie sollten jedoch darüber nachdenken, die Teile, die Ihre API aufrufen, neu zu schreiben, damit sie auch ein bisschen sauberer sind.

Es gibt viele Bereiche von Geschäftstools, die irreduzibel komplex sind. Sie können nicht alle Komplexität loswerden. Sie können es jedoch verwalten, indem Sie sich auf saubere APIs konzentrieren, die genau das tun, was Sie tun müssen, und auf Module, die diese API konstruktiv nutzen. Kleber sollte ein letzter Ausweg sein, wenn man bedenkt, dass das Umschreiben des restlichen aufrufenden Codes möglicherweise schneller ist.

Chris Travers
quelle
Ich denke, Sie haben vielleicht den Nagel auf den Kopf getroffen. Der Grund, warum der Klebstoff vorhanden ist, liegt möglicherweise am Code, der die von mir erstellte Schnittstelle aufruft. Ich werde auf weitere Antworten warten, um zu sehen, ob wir etwas verpassen, aber ich glaube, das fasst es ganz gut zusammen.
cmhobbs
1
"Ausführungspfade mäandern zwischen Modulen und zurück und zwischen Sprachen, ohne Reim oder Grund" - das erinnert mich auch an einige moderne OO-Praktiken.
user253751
8

Es hört sich so an, als ob Sie ein Wirrwarr aus einer vor- uralen Codebasis genommen und eine schöne modulare vor- urale Codebasis erstellt haben.

Ausnahmslos habe ich eine saubere API und keine wirkliche Möglichkeit, alles zusammenzubinden. Die Lösung bestand darin, eine große hässliche "Kleber" -Methode (im Allgemeinen voll von bedingten Anweisungen) zu schreiben, die schließlich alle meine "sauberen" Methoden aufruft.

Mit prozeduralem Code (auch wenn er als OO getarnt ist) wird immer irgendwo ein sequentieller Workflow definiert, der häufig mit komplexen bedingten Verzweigungen gefüllt ist, wie Sie beschreiben. Ich vermute, es ist diese prozedurale Natur des Codes, die Sie das Gefühl gibt, dass etwas nicht stimmt. Dies ist nicht unbedingt eine schlechte Sache, und wenn Sie mit Legacy-Code arbeiten, ist dies möglicherweise unvermeidlich

MattDavey
quelle
6

Sie sollten die große hässliche Klebemethode genauso bereinigen, wie Sie die ursprüngliche Codebasis bereinigt haben. Teilen Sie es in übersichtliche modulare Methoden auf. Sie haben wahrscheinlich Gruppen von Codezeilen, die einige Aufgaben erledigen, die diese Zeilen in Methoden aufteilen. Wenn Sie einige Variablen gemeinsam nutzen, können Sie erwägen, die gemeinsam genutzten Variablen und die neuen Methoden in eine Klasse einzuteilen.

Erblassen
quelle
2
Bekommst du dann keinen Leimbaum?
Pieter B
3
@PieterB vielleicht, aber es ist einfacher, die verschiedenen Abhängigkeiten zu extrahieren, wenn Sie die verschiedenen Aufgaben in verschiedenen Methoden haben. Sie können nach dem Extrahieren der neuen Methoden einen weiteren Refactoring-Durchgang durchführen.
Paling
1

Grundsätzlich fügen Sie immer wieder Abstraktionsebenen hinzu, bis jede Ebene für sich betrachtet wird . Das Paradoxe an der Abstraktion ist, dass Sie die Komplexität erhöhen, um sie zu reduzieren, da Sie sich beim Lesen von abstrahiertem Code immer nur mit einer Ebene beschäftigen. Wenn jede Schicht klein genug ist, um leicht verstanden zu werden, spielt es keine Rolle, auf wie vielen Schichten sie ruht.

Das macht es auch schwierig, Abstraktionen zu schreiben. Sogar etwas so Einfaches wie ein Bleistift kann den Verstand beugen, wenn Sie versuchen, alle Abstraktionsebenen gleichzeitig in Ihrem Kopf zu halten. Der Schlüssel besteht darin, eine Ebene so zu erstellen, wie Sie es möchten, und dann die Komplexität zu vergessen, die dieser Ebene zugrunde liegt, und dasselbe auf der nächsten Ebene zu tun.

Karl Bielefeldt
quelle
0

Es hört sich so an, als würden Sie die API umgestalten, indem Sie nur an die Implementierung der API denken, aber nicht genug über den Code nachdenken, der die API verwendet - also den "Klebercode", von dem Sie sprechen.

Wenn das stimmt, versuchen Sie vielleicht, am anderen Ende zu beginnen. Schreiben Sie die Dinge, die zu Ihrem hässlichen Code werden könnten, neu und erstellen Sie einige noch nicht implementierte Schnittstellen, die in diesem Prozess zu Ihrer API werden. Denken Sie noch nicht zu viel über die tatsächliche Implementierung dieser API nach - es ist in Ordnung, wenn Sie das Gefühl haben, dies zu tun. Und erst dann schreiben Sie das Codelabyrinth neu, damit es mit dieser API übereinstimmt. Natürlich wird es in diesem Prozess einige Änderungen an der API und am Klebercode geben, aber es sollte besser zusammenpassen.

Hans-Peter Störr
quelle