Rebuses im Code und wie man sie entschlüsselt. Die geheime Kraft der Identifikatoren

Reiner Code liest sich wie gut geschriebene Prosa.
Grady Butch in sauberem Code

Rebus als Code




Was ist ein Rebus? Dies ist eine verschlüsselte Nachricht. Der Autor des Rebus nimmt gewöhnlichen menschlichen Text und verschlüsselt ihn mit Zeichnungen, Zahlen und Buchstaben. Und wir schauen uns eine solche Verschlüsselung an und versuchen, den Quelltext zu lesen.

Der Rebus hat zwei Formen. Der Rebus ist einerseits der unverschlüsselte Originaltext und andererseits die Chiffrierzeichnungen. Der Text ist das „Was“ des Rebus, seine Bedeutung, Botschaft. Bilder sind „wie“: wie genau die Nachricht verschlüsselt wird, mit welchen Mitteln. Wenn wir den Rebus erraten, übersetzen wir „wie“ in „was“.

Zeichnungen sind die Sprache des Rebus, sein Arsenal an Ausdrucksmitteln. Der Rebusnik spricht sozusagen mit Hilfe dieser Zeichnungen zu uns, kommuniziert etwas. Er darf keine normalen menschlichen Wörter verwenden.

So lesen sich die Rätsel:



Der Code ist wie ein Rebus


Der Programmcode hat etwas mit dem Rebus gemeinsam: Er hat auch ein eigenes „Was“ und „Wie“. Und manchmal muss es auch entschlüsselt werden.

Das „Was“ eines Codes ist sein Zweck, dh dieser Effekt und das Endergebnis, das wir von ihm erwarten. Was genau macht er ?

"Wie" des Codes - auf welche konkrete Weise wird er sein "Was" erfüllen, mit welchen spezifischen Zuordnungen, Multiplikationen, Vergleichen; Implementierung des Algorithmus, Anweisungen an den Prozessor. Es ist eine erlaubte Codesprache, ein Arsenal an Ausdrucksmitteln.

Martin Fowler spricht so darüber ( „Funktionslänge“ , Original ):
Smalltalk arbeitete in jenen Jahren an Schwarz-Weiß-Maschinen. Wenn Sie Text oder Grafiken hervorheben mussten, mussten Sie das Video umkehren. Die Klasse in Smalltalk, die für den Zeitplan verantwortlich war, enthielt die 'Highlight'-Methode, und in ihrer Implementierung gab es nur eine Zeile - einen Aufruf der' Reverse'-Methode. Der Name der Methode war länger als die Implementierung, aber es spielte keine Rolle, da zwischen der Absicht und der Implementierung dieses Codes ein großer Abstand besteht.
Hier ist das Highlight das „Was“. In Martins Terminologie - Absicht : "Markieren Sie ein Fragment des Bildes." Der Name drückt aus, was diese Funktion tut. Umgekehrt ist wie, Implementierung . Wie genau die Hervorhebung durchgeführt wird (mithilfe der Bildinversion). Das ist der Unterschied zwischen "was" und "wie".

Trotz der Tatsache, dass der Name der Methode länger ist als ihre Implementierung, ist die Existenz einer solchen Methode aus einem sehr einfachen Grund sinnvoll. Wenn wir einen umgekehrten Aufruf im Code sehen, müssen wir verstehen oder uns daran erinnern, dass die Bildinversion verwendet wird, um dieses Bild besser sichtbar zu machen. Wenn wir ein Highlight sehen, lesen wir einfach: "Machen Sie dieses Fragment sichtbarer." Im ersten Fall geben wir uns ein wenig Mühe, um die dem Code zugewiesene Mission zu verstehen, im zweiten - nein. Im ersten Fall sehen wir einen Rebus vor uns, der entschlüsselt werden muss, im zweiten Fall eine Geschichte in einer verständlichen Sprache.

Ein Programmierer ist, wenn er ein Programm schreibt, wie ein Rebus. Der Programmierer verschlüsselt die menschliche Beschreibung des Algorithmus mit verfügbaren Programmiersprachenwerkzeugen (primitiver als die menschliche Sprache). Verschlüsselt "was" mit "wie". Und später liest er oder sein Kollege den Code und entschlüsselt diese Rebusse, die anfängliche Beschreibung des Algorithmus. Wenn wir beim Lesen des Codes nicht sofort verstehen können, zu welchem ​​Ergebnis die Ausführung jedes Fragments führen wird, dh zu welchem ​​Zweck und welcher Bedeutung der Code, dann ist dieser Code ein Rebus und muss in einer klaren Sprache neu geschrieben werden.

Das Problem mit den Rätseln im Code ist, dass sie immer mentale Anstrengung erfordern. Selbst wenn wir nicht alle Entschlüsselungsvorgänge in unserem Kopf ausführen, sondern uns nur dumm an die Bedeutung eines Rebus erinnern, wird dies dennoch eine Last erzeugen: Erstens, um sich an seine Bedeutung zu erinnern, und zweitens zum Zeitpunkt der Aufzeichnung der Transformation Rebus in diesem Wert.

Das Entschlüsseln des Rebus beim Lesen des Codes ist die mentale Transformation, über die Tim Ottinger in dem Buch Clean Code spricht. Zwar diskutiert er sie dort im Zusammenhang mit der Zuweisung verständlicher Namen zu Variablen, aber das Problem ist von genau demselben betroffen. Wort an Tim:
Programmierer sind in der Regel sehr schlau. Und kluge Leute zeigen manchmal gerne die Kraft der Intelligenz und demonstrieren ihre Fähigkeit, mental zu jonglieren. Wenn Sie sich am Ende daran erinnern, dass die Variable r eine URL mit einem Remote-Host und ein in Kleinbuchstaben konvertiertes Schema enthält, ist dies ein klarer Hinweis auf Ihre Meinung.
Einer der Unterschiede zwischen einem intelligenten und einem professionellen Programmierer besteht darin, dass ein Fachmann versteht: Klarheit ist von größter Bedeutung. Profis nutzen ihre Macht für immer und schreiben Code, der für andere Menschen verständlich ist.
Selbst eine kleine Ladung von jedem Rebus kann zu einem Problem werden, wenn es viele solcher Rebusse gibt. Sie sind wahrscheinlich auf Code gestoßen, dessen Lesen einfach anstrengend ist. Wissen Sie: Rebusse im Code sind für Ihre Müdigkeit verantwortlich. Rebuses verschärfen die Müdigkeit selbst des eigenen Autors beim Schreiben von Code. Schließlich liest der Programmierer beim Schreiben von Code auch immer wieder neu, was geschrieben wurde. Trotz der Tatsache, dass der Autor seine eigenen Rätsel nicht entschlüsselt, sondern sich nur daran erinnert, erzeugen sie immer noch eine Last. Die Falle ist, dass der Autor einfach keine Rätsel in seinem eigenen Code sieht ! Versuchen Sie sich vorzustellen, wie viel geistige Anstrengung Sie am Abend sparen können, wenn Sie morgens anfangen, Rätsel in Ihrem Code loszuwerden!

Um die Müdigkeit beim Schreiben und Lesen von Code zu verringern, müssen Sie Rätsel vermeiden. Aber wie geht das?

Die Sprache des Codes. Identifikatorstärke


Ich stimme der Aussage von Grady Butch zu, dass sauberer Code wie gute Prosa liest. Dies ist eine notwendige, wenn auch nicht ausreichende Bedingung. Die meisten von uns werden intuitiv verstehen, worum es geht, aber ich möchte zumindest eine Definition erhalten: Was ist das - gute Prosa.

Ich fragte meine Kollegen: Wie unterscheidet sich gute Prosa von schlechter Prosa? Jeder antwortete anders, betonte aber irgendwie die Bedeutung der Sprache: Sie muss reich sein, sie muss klare Bilder in Geist und Seele des Lesers erzeugen. Wenn der Leser leicht ein klares Bild hat, das der Autor für ihn zeichnen wollte, haben wir es mit guter Prosa zu tun.

Der Code teilt dem Prozessor mit, was er tun soll. Ein guter Code sagt es gleichzeitig dem Programmierer - und darüber hinaus sehr wahrheitsgemäß! - Was macht er hier? Das heißt, es legt seinen Algorithmus so nahe wie möglich daran, wie der Autor es in einer natürlichen Sprache getan hätte. Unser Code muss dies sehr gut tun, sonst kann ein ungezügelter Verrückter mit einer Kettensäge oder einer Schrotflinte zu uns nach Hause kommen. Der Code sollte kein Rebus sein.

Welche Tools hat der Code, um kein Rebus zu sein?

Eine Geschichte im Namen des Codes wird von einer Person leicht verstanden, wenn der Code selbst in menschlicher Sprache spricht. Dies kann nur mit Hilfe von Bezeichnern erreicht werden: Namen von Funktionen, Klassen, Variablen und Konstanten - denn nur in Bezeichnern können wir Wörter der menschlichen Sprache verwenden, die wir benötigen .

Natürlich sind die Schlüsselwörter einer Programmiersprache auch menschliche Wörter, aber ihr Wortschatz ist zu elend. So etwas wie die Sprache von Ellochka dem Oger - man kann keine gute Prosa darüber schreiben.

Daher ist es wichtig, dass der Programmcode so viele korrekt ausgewählte Bezeichner wie möglich enthält. Damit ihre Gesamtheit die sehr gut geschriebene Prosa bildet.

Sehen Sie, wie einfach es ist, Codezeilen zu lesen, wenn die Namen der Variablen und Methoden gut ausgewählt sind:

pageData.hasAttribute("Test") dom_tree.to_html() emails_str.split(',') 

Wenn man sich diese kurzen Sätze ansieht, ist es leicht zu verstehen, wovon sie sprechen. Wir wissen, welches Ergebnis wir erhalten, weil uns Identifikatoren davon erzählen. Stellen Sie sich nun vor, dass an der Stelle jedes solchen Aufrufs seine Implementierung erfolgt - um wie viel verringert sich die Lesegeschwindigkeit eines solchen „verschlüsselten“ Codes?

Bei vielen der einfachsten Refactoring-Techniken: benannte Konstanten, Auswahl einer Methode, Ersetzen einer Variablen durch einen Methodenaufruf, eine erklärende Variable, Aufteilen einer temporären Variablen usw. geht es darum, wie der Code die menschliche Sprache spricht, mit anderen Worten, wie Rätsel vermieden werden .

Rebus-Methode


Als ich Pure Code las, wurde ich regelmäßig von dem Gedanken besucht: „Was zur Hölle!“.

Robert Martin gibt uns aufgrund seiner 40-jährigen Erfahrung Tipps, wie man Code verbessern kann. Zum Beispiel:
Erste Regel: Funktionen sollten kompakt sein. Die zweite Regel: Funktionen sollten noch kompakter sein.
Und dann gibt er zu, dass er seine Behauptung nicht wissenschaftlich begründen kann. Ehrlich gesagt, unwissenschaftlich, macht er es auch schlecht. Das Erfordernis der Funktionskompaktheit sieht bereits wie ein Dogma aus - deshalb ist die Debatte über die Frage nach der Länge einer Funktion seit so vielen Jahrzehnten nicht mehr abgeklungen.

Und Bob bietet auch an, jede Funktion so zu schreiben, dass sie nur eine Operation ausführt. Darüber hinaus ist auch nicht klar, was diese eine Operation ist. Wir müssen das Prinzip einer einzigen Abstraktionsebene um Hilfe bitten, was die Situation weiter verwirrt. Das alles ist zu neblig.

Martin Fowler ist pragmatischer.

Es scheint mir, dass das Argument über die Trennung von Absicht und Verwirklichung mehr Bedeutung hat. Wenn Sie sich beim Betrachten eines Codes bemühen müssen, zu verstehen, was er tut, müssen Sie ihn in eine Funktion einfügen und ihm einen Namen geben, der diesem „Was“ entspricht. Beim nächsten Mal ist der Zweck der Funktion sofort ersichtlich, und in den meisten Fällen ist es Ihnen egal, wie die Funktion ihre Aufgabe erfüllt.

Das Original
Das für mich sinnvollste Argument ist jedoch die Trennung zwischen Absicht und Umsetzung. Wenn Sie sich die Mühe machen müssen, ein Codefragment zu betrachten, um herauszufinden, was es tut, sollten Sie es in eine Funktion extrahieren und die Funktion nach diesem „Was“ benennen. Auf diese Weise springt der Zweck der Funktion beim erneuten Lesen direkt auf Sie zu, und die meiste Zeit müssen Sie sich nicht darum kümmern, wie die Funktion ihren Zweck erfüllt - das ist der Hauptteil der Funktion.
Schon besser. Sehen Sie jetzt, was Martin in dieser Passage sagen wollte? Er meinte: Lass uns die Rätsel beseitigen. Lassen Sie den Code selbst uns sagen, was das Ergebnis sein wird und wie - lassen Sie es irgendwo weiter entfernt in der Definition der Funktion versteckt sein. Lassen Sie alle Rätsel entschlüsselt werden. Keine Rätsel - keine Anstrengung.

In keinem Fall sollten Sie Refactoring-Methoden blind anwenden. Das ist so offensichtlich, aber wie kann man verstehen, wann Refactoring wirklich benötigt wird und wann nicht? Die Rebus-Methode besagt: Wenn der Rebus nach dem Refactoring nicht verschwindet, ist kein Refactoring erforderlich .

Wenn Sie keinen Namen für eine neue Funktion finden, der klar erklärt, was darin passiert, ist es eine Glocke, dass Sie hier etwas falsch machen. Versuchen Sie, ein etwas anderes Codefragment in der Funktion auszuwählen, für das Sie schnell einen verständlichen und kurzen Namen finden.

Ein Beispiel für das Entschlüsseln von Rätseln im Code (nicht sehr erfolgreich)


Als solches werde ich ein Fragment aus dem Buch „Clean Code“ zitieren, das mir gefallen hat. Vor dem Refactoring sehen wir Code voller Rätsel. Das Refactoring wurde vom Autor des Buches gemäß den von ihm beworbenen Regeln für guten Code durchgeführt, und - nur ein Zufall - der refactored Code sieht genauso aus wie der Code, in dem die Rebusse entschlüsselt werden.

Der Refactoring-Autor hat vollständig lesbare Bezeichner (Klassennamen, Methoden und Variablen) angewendet, um anzugeben, was der Code tatsächlich tut. Es ist schade, dass es nicht überall erfolgreich war und an einigen Stellen anstelle der vorherigen Rätsel neue Rätsel auftauchten.

Zum Beispiel die in dieser Passage am häufigsten verwendete Include-Methode

 private void include(String pageName, String arg) throws Exception { WikiPage inheritedPage = findInheritedPage(pageName); if (inheritedPage != null) { String pagePathName = getPathNameForPage(inheritedPage); buildIncludeDirective(pagePathName, arg); } } 

Der Name spiegelt überhaupt nicht wider, was in der Implementierung passiert. Was beinhaltet und wo?

Betrachten Sie den Aufruf dieser Methode:

 include("TearDown", "-teardown"); 

Es ist unmöglich zu sagen, welches Ergebnis der Autor des Codes hier erzielen würde.

Weiter: Was macht buildIncludeDirective? Nach dem Namen zu urteilen, sollte er eine Art Einschlussrichtlinie ausarbeiten, und was nun? Bring sie zurück? Aber nein. Er fügt es sofort zum Gesamtergebnis hinzu.

Und hier ist noch ein updatePageContent. Was sagt uns updatePageContent über das Ergebnis, das wir nach dem Aufruf der Methode erhalten? Nichts. Einige Seiteninhalte werden dort ersetzt, wenn niemand weiß was. Warum wurde hier Refactoring als Methodenextraktion bezeichnet? Hat er geholfen, den Rebus loszuwerden? Es hat nicht geholfen, aber den Code nur noch mehr verwirrt. Hier haben wir genau den Fall, in dem der Körper der Methode vorzuziehen ist. Bau

 pageData.setContent(newPageContent.toString()); 

viel klarer als das kryptische updatePageContent ().

Zur Unterhaltung schlage ich den Lesern vor, nach anderen schlechten Stellen im überarbeiteten Code zu suchen.

Um Bob zu rechtfertigen, kann ich sagen, dass dieser Code nicht mehr in der aktuellen Version von FitNesse enthalten ist. Anscheinend wurde auch er einmal umgestaltet.

Fazit


Die Länge der Funktion ist ein zu vages Kriterium, um die Qualität der Funktion zu bestimmen. "Kurze Funktionen" ist nicht gleich "gute Funktionen". Die Länge der Funktion ist kein Kriterium, vergessen Sie alles.

Guter Code sollte Antworten auf die Fragen des Programmierers geben - warum ist er (der Code) hier, was zum Teufel macht er hier, er erzielt das Ergebnis. Diese Antworten können nur mit Bezeichnern gegeben werden.

Als Beispiel dafür, welche Art von Antworten der Code nicht geben sollte, möchte ich einen Auszug aus einem lustigen Buch geben.
"Ich bin Ronan, Victor of Evil", sagte er langsam. - Und das ist Tarl. Wir wollen dich
Fragen stellen. Wenn du lügst, stirbst du. Verstanden
"Ich, Onkel, für immer", hauchte er. - Bitte. Ich werde alles sagen.
"Das ist schön", fuhr Ronan fort. - Name?
- Ronan, Gewinner des Bösen.
- Ja, nicht meins, Idiot!
"Ah, ja, dann Tarle", antwortete der Ork entschuldigend.
- Und nicht meins! Gemurmelte Tarle. - Dein Name, Verein! Vorname!
„Der Name ist der Name, mit dem ich mich von anderen unterscheide“, murmelte der Ork.
- Nun, gib diesen Namen hier! Schrie Tarle.
Orka dämmerte plötzlich.
- Ah! Pickel!
"Also Pickel, was machst du hier?"
"Ich habe es in meine Hose gesteckt", kam die wahrheitsgemäße Antwort.
Ronan runzelte angewidert die Nase.
"Nein, ich frage, was deine Orkbande hier macht!"
Pimples Augen drehten sich schnell um und sahen sich in der Szene um.
"Die meisten Leute sind hier kopflos", murmelte er.
Tarle berührte Ronan an der Schulter.
„Lass es mich versuchen“, sagte er zuversichtlich und wandte sich an den verängstigten Ork. - Sag mir,
Pickel ", fuhr er fort," warum bist du hier? "
- Oh Onkel, und frag nicht. Existenzphilosophie ist für mich nur ein dunkler Wald.
„Hör zu, du Drachenrülpsen“, knurrte er gedämpft. - Deine Orkbande hatte etwas Besonderes
Grund hierher zu kommen. Was ist es im Wald?
- Es gibt viele Bäume.
Ronans Augen weiteten sich und Tarle wandte sich ab. Der Pickel, der das Gefühl hatte, nicht die erwartete Antwort gegeben zu haben, begann weiter zu murmeln.
- Und wenn Sie den Grund und nicht den Wald kennen wollen, dann nur, weil diese Person in der Kneipe ist
hat uns bezahlt, hierher zu kommen und dich zu töten.
James Bibby, Ronan der Barbar

Source: https://habr.com/ru/post/de419269/


All Articles