Zen gehen



Bei der Bewertung meiner Arbeit habe ich kürzlich viel darüber nachgedacht, wie man guten Code schreibt. Da niemand daran interessiert ist, wie man schlechten Code schreibt , stellt sich die Frage: Woher wissen Sie, ob Sie guten Code auf Go geschrieben haben ? Wenn es eine Art Skala zwischen gut und schlecht gibt, wie kann man dann verstehen, welche Teile der Skala zu den guten gehören? Was sind seine Eigenschaften, Attribute, Unterscheidungsmerkmale, Muster und Redewendungen?

Idiomatisch gehen


Diese Überlegungen führten mich zum idiomatischen Go. Wenn wir etwas „idiomatisch“ nennen, dann entspricht dieses etwas einem bestimmten Stil einiger Zeit. Wenn etwas nicht idiomatisch ist, entspricht es nicht dem dominanten Stil. Das ist nicht in Mode.

Noch wichtiger ist, wenn wir sagen, dass der Code einer Person nicht idiomatisch ist, erklärt dies nicht den Grund. Warum nicht idiomatisch? Die Antwort gibt das Wörterbuch.

Redewendung (S.): Eine Sprachrevolution, die als Ganzes verwendet wird, keiner weiteren Zersetzung unterliegt und normalerweise keine Permutationen in sich zulässt.

Redewendungen sind Kennzeichen gemeinsamer Bedeutungen. Bücher werden dir nicht das idiomatische Gehen beibringen, es ist nur bekannt, wenn du Teil einer Gemeinschaft wirst.

Ich mache mir Sorgen um das idiomatische Go-Mantra, weil es oft restriktiv ist. Sie sagt: "Du kannst nicht bei uns sitzen." Ist es nicht das, was wir meinen, wenn wir die Arbeit eines anderen als „nicht idiomatisch“ kritisieren? Sie haben es falsch gemacht. Das sieht nicht richtig aus. Dies entspricht nicht dem Stil der Zeit.

Ich glaube, dass das idiomatische Go nicht geeignet ist, um zu lehren, wie man guten Code schreibt, weil es im Wesentlichen bedeutet, den Leuten zu sagen, dass sie etwas falsch gemacht haben. Es ist besser, solche Ratschläge zu geben, die eine Person nicht in dem Moment wegschieben, in dem sie diesen Rat am liebsten erhalten möchte.

Sprüche


Lassen Sie uns von idiomatischen Problemen ablenken. Welche anderen kulturellen Artefakte sind Go-Programmierern eigen? Wenden Sie sich an die schöne Seite Go Proverbs . Sind diese Sprüche ein geeignetes Lernwerkzeug? Sagen sie Anfängern, wie man guten Go-Code schreibt?

Ich glaube nicht. Ich möchte die Arbeit des Autors nicht herabsetzen. Die Sprüche, die er verfasst hat, sind lediglich Beobachtungen, keine Bedeutungsdefinitionen. Das Wörterbuch kommt wieder zur Rettung:

Sprichwort (S.): Eine kurze Aussage, die wörtliche oder bildliche Bedeutung hat.

Die Mission von Go Proverbs ist es, die tiefe Essenz der Spracharchitektur zu zeigen. Aber wird es nützlich sein, einem Anfänger, der aus einer Sprache ohne strukturelle Typisierung stammt , Ratschläge wie "Die leere Oberfläche sagt nichts " zu geben?

In einer wachsenden Gemeinschaft ist es wichtig zu erkennen, dass die Anzahl der Go-Schüler weitaus größer ist als die Anzahl derjenigen, die diese Sprache fließend sprechen. Das heißt, Sprüche sind wahrscheinlich nicht der beste Weg, um in einer solchen Situation zu lernen.

Designwerte


Dan Liu fand eine alte Präsentation von Mark Lukowski über die Designkultur im Windows NT-Windows 2000 Windows-Entwicklungsteam. Ich erwähnte dies, weil Lukowski Kultur als eine übliche Methode zur Bewertung von Architekturen und zum Eingehen von Kompromissen beschreibt.


Die Hauptidee besteht darin , wertebasierte Entscheidungen innerhalb einer unbekannten Architektur zu treffen . Das NT-Team hatte folgende Werte: Portabilität, Zuverlässigkeit, Sicherheit und Erweiterbarkeit. Einfach ausgedrückt, sind Designwerte ein Weg, um Probleme zu lösen.

Werte gehen


Was sind die expliziten Werte von Go? Was sind die Schlüsselkonzepte oder -philosophien, die bestimmen, wie Go-Programmierer die Welt interpretieren? Wie werden sie verkündet? Wie werden sie unterrichtet? Wie werden sie verfolgt? Wie verändern sie sich im Laufe der Zeit?

Wie konvertiert man einen Go-Programmierer, um die Werte des Go-Designs zu erhalten? Oder wie verkünden Sie als erfahrener Go-Pro Ihre Werte zukünftigen Generationen? Und damit Sie verstehen, ist dieser Prozess des Wissenstransfers nicht optional? Ohne den Zustrom neuer Teilnehmer und neuer Ideen wird unsere Community kurzsichtig und verdorrt.

Werte anderer Sprachen


Um den Weg für das vorzubereiten, was ich sagen möchte, können wir auf andere Sprachen achten, auf ihre Designwerte.

In C ++ und Rust wird beispielsweise angenommen, dass ein Programmierer nicht für eine Funktion bezahlen sollte, die er nicht verwendet . Wenn das Programm keine ressourcenintensive Funktion der Sprache verwendet, kann das Programm nicht gezwungen werden, die Kosten für die Wartung dieser Funktion zu tragen. Dieser Wert wird aus der Sprache in die Standardbibliothek projiziert und als Kriterium für die Bewertung der Architektur aller in C ++ geschriebenen Programme verwendet.

Hauptwert in Java, Ruby und Smalltalk - alles ist ein Objekt. Dieses Prinzip liegt dem Programmdesign in Bezug auf Nachrichtenübertragung, Ausblenden von Informationen und Polymorphismus zugrunde. Architekturen, die einem prozeduralen oder funktionalen Paradigma entsprechen, werden in diesen Sprachen als fehlerhaft angesehen. Oder, wie ein Go-Programmierer sagen würde, nicht idiomatisch.

Kommen wir zurück zu unserer Community. Welche Designwerte bekennen sich Go-Programmierer? Die Diskussionen zu diesem Thema sind häufig fragmentiert, so dass es nicht einfach ist, eine Reihe von Bedeutungen zu formulieren. Es ist unbedingt erforderlich, eine Einigung zu erzielen, aber die Schwierigkeit, eine Einigung zu erzielen, wächst exponentiell mit der wachsenden Anzahl von Teilnehmern an der Diskussion. Aber was ist, wenn jemand diesen schwierigen Job für uns gemacht hat?

Zen Python Go


Vor einigen Jahrzehnten setzte sich Tim Peters hin und schrieb PEP-20 - The Zen of Python . Er versuchte, die Designwerte zu dokumentieren, an denen Guido Van Rossum als großzügiger lebenslanger Python-Diktator festhielt.

Schauen wir uns das Zen von Python an und sehen wir, ob wir etwas über die Designwerte des Go-Designers erfahren können.

Ein gutes Paket beginnt mit einem guten Namen


Beginnen wir mit dem Scharfen:

Namespaces sind eine großartige Idee, machen wir sie größer!

Das Zen von Python, Rekord 19.

Eindeutig genug: Python-Programmierer sollten Namespaces verwenden. Viele Räume.

In der Go-Terminologie ist ein Namespace ein Paket. Es besteht kein Zweifel, dass die Bündelung Design und Wiederverwendung begünstigt. Es kann jedoch zu Verwirrung darüber kommen, wie dies zu tun ist, insbesondere wenn Sie über langjährige Programmiererfahrung in einer anderen Sprache verfügen.

In Go muss jedes Paket für etwas entworfen werden. Und der Name ist der beste Weg, um dieses Ziel zu verstehen. Um Peteres 'Gedanken neu zu formulieren, sollte jedes Paket in Go auf eine Sache ausgelegt sein.

Die Idee ist nicht neu, darüber habe ich bereits gesprochen . Aber warum sollte dieser Ansatz verwendet werden und nicht ein anderer, bei dem Pakete für die Anforderungen einer detaillierten Klassifizierung verwendet werden? Es geht nur um die Veränderungen.

— , .


Veränderung ist der Name des Spiels, an dem wir teilnehmen. Wir als Programmierer verwalten Veränderungen. Wenn wir es gut machen, nennen wir es Architektur. Und wenn es schlecht ist, nennen wir es technische Schulden oder Legacy-Code.

Wenn Sie ein Programm schreiben, das einmal mit einem festen Satz von Eingabedaten hervorragend funktioniert, ist niemand daran interessiert, ob es guten Code enthält, da nur das Ergebnis seiner Arbeit für das Geschäft wichtig ist.

Das passiert aber nicht . Es gibt Fehler in Programmen, Anforderungen und Änderungen der Eingabedaten, und nur sehr wenige Programme werden mit einer einzigen Ausführungserwartung geschrieben. Das heißt, Ihr Programm wird sich im Laufe der Zeit ändern. Vielleicht wird Ihnen diese Aufgabe übertragen, aber höchstwahrscheinlich wird es jemand anderes tun. Jemand muss diesen Code begleiten.

Wie erleichtern wir den Programmwechsel? Überall Schnittstellen hinzufügen? Tun Sie alles, was zum Erstellen von Stubs geeignet ist? Abhängigkeiten eng bereitstellen? Vielleicht sind diese Techniken für einige Arten von Programmen geeignet, aber nicht für viele. Für die meisten Programme ist das Erstellen einer flexiblen Architektur jedoch mehr als Design.

Und wenn wir die Komponenten nicht erweitern, sondern ersetzen? Wenn die Komponente nicht das tut, was in den Anweisungen angegeben ist, ist es Zeit, sie zu ändern.

Ein gutes Paket beginnt mit der Auswahl eines guten Namens. Betrachten Sie es als eine kurze Präsentation, die die Funktion eines Pakets mit nur einem Wort beschreibt. Und wenn der Name die Anforderung nicht mehr erfüllt, suchen Sie einen Ersatz.

Einfachheit ist wichtig


Einfach ist besser als komplex.

Das Zen von Python, Eintrag 3.

PEP-20 behauptet, dass das Einfache besser ist als das Komplexe, und ich stimme vollkommen zu. Vor ein paar Jahren schrieb ich:


Die meisten Programmiersprachen versuchen zunächst einfach zu sein, entscheiden sich aber später dafür, leistungsfähig zu sein.

Nach meinen Beobachtungen konnte ich mich zumindest zu dieser Zeit nicht an eine Sprache erinnern, die ich kannte und die nicht als einfach angesehen werden würde. Als Rechtfertigung und Versuchung erklärten die Autoren jeder neuen Sprache die Einfachheit. Aber ich fand, dass Einfachheit nicht der Kernwert vieler Sprachen im gleichen Alter wie Go war (Ruby, Swift, Elm, Go, NodeJS, Python, Rust). Vielleicht trifft dies einen wunden Punkt, aber vielleicht liegt der Grund darin, dass keine dieser Sprachen einfach ist. Oder ihre Autoren hielten sie nicht für einfach. Einfachheit wurde nicht in die Liste der Kernwerte aufgenommen.

Sie können mich als altmodisch betrachten, aber wann ist diese Einfachheit aus der Mode gekommen? Warum vergisst die kommerzielle Softwareindustrie diese grundlegende Wahrheit ständig und freudig?

Es gibt zwei Möglichkeiten, eine Softwarearchitektur zu erstellen: Sie ist so einfach, dass das Fehlen von Fehlern offensichtlich ist, und sie ist so komplex, dass sie keine offensichtlichen Fehler aufweist. Die erste Methode ist viel schwieriger.

Charles Hoar, Die alte Kleidung des Kaisers, Turing Award Lecture, 1980

Einfach heißt nicht einfach, das wissen wir. Oft ist mehr Aufwand erforderlich, um die Benutzerfreundlichkeit zu gewährleisten, als die einfache Erstellung.

Einfachheit ist der Schlüssel zur Zuverlässigkeit.

Edsger Dijkstra, EWD498, 18. Juni 1975

Warum nach Einfachheit streben? Warum ist es wichtig, dass Go-Programme einfach sind? Einfach bedeutet roh, lesbar und leicht zu befolgen. Einfach bedeutet nicht kunstlos, sondern zuverlässig, verständlich und verständlich.

Der Kern der Programmierung ist das Komplexitätsmanagement.

Brian Kernigan, Software Tools (1976)

Ob Python seinem Mantra der Einfachheit folgt, ist eine umstrittene Frage. Bei Go ist Einfachheit jedoch ein zentraler Wert. Ich denke, wir sind uns alle einig, dass in Go einfacher Code dem intelligenten Code vorzuziehen ist.

Vermeiden Sie Zustände auf Paketebene


Explizit ist besser als implizit.

Das Zen von Python, Eintrag 2

Hier träumt Peters meiner Meinung nach eher, als sich an die Fakten zu halten. In Python ist vieles nicht explizit: Dekorateure, Dunder-Methoden usw. Zweifellos sind dies mächtige Werkzeuge, und sie existieren aus einem bestimmten Grund. An der Implementierung jedes besonders komplexen Features hat jemand gearbeitet. Die aktive Verwendung solcher Funktionen macht es jedoch schwierig, die Kosten der Operation beim Lesen des Codes zu bewerten.

Glücklicherweise können wir Go-Programmierer den Code optional explizit machen. Vielleicht ist Manifestation für Sie gleichbedeutend mit Bürokratie und Ausführlichkeit, aber dies ist eine oberflächliche Interpretation. Es wird ein Fehler sein, sich nur auf die Syntax zu konzentrieren, sich um die Länge der Zeilen und die Anwendung der DRY-Prinzipien auf Ausdrücke zu kümmern. Es scheint mir wichtiger, explizite Aussagen in Bezug auf Verbundenheit und Zustände zu machen.

Die Konnektivität ist ein Maß für die Abhängigkeit des einen vom anderen. Wenn eines eng mit dem anderen verwandt ist, bewegen sich beide zusammen. Eine Handlung, die das eine betrifft, spiegelt sich direkt im anderen wider. Stellen Sie sich einen Zug vor, in dem alle Wagen miteinander verbunden sind. Wo der Dampfzug fährt, sind die Autos.

Konnektivität kann auch mit dem Begriff Kohäsion - Kohäsion beschrieben werden. Dies ist ein Maß dafür, wie sehr einer zum anderen gehört. In einem gelöteten Team sind alle Teilnehmer so aufeinander abgestimmt, als wären sie speziell dafür geschaffen worden.

Warum ist Kohärenz wichtig? Wie im Fall des Zuges müssen Sie, wenn Sie einen Code ändern müssen, den Rest des eng verwandten Codes ändern. Zum Beispiel hat jemand eine neue Version seiner API veröffentlicht, und jetzt wird Ihr Code nicht kompiliert.

Eine API ist eine unvermeidbare Bindungsquelle. Aber es kann in heimtückischeren Formen präsentiert werden. Jeder weiß, dass sich auch die zur und von der API übertragenen Daten ändern, wenn sich die Signatur der API geändert hat. Es geht nur um die Funktionssignatur: Ich nehme die Werte eines Typs und gebe die Werte anderer Typen zurück. Und wenn die API beginnt, Daten auf andere Weise zu übertragen? Was ist, wenn das Ergebnis jedes API-Aufrufs vom vorherigen Aufruf abhängt, auch wenn Sie Ihre Einstellungen nicht geändert haben?

Dies nennt man Staat, und Staatsmanagement ist ein Problem in der Informatik.

package counter

var count int

func Increment(n int) int {
        count += n
        return count
}

Hier haben wir ein einfaches Paket counter. Um den Zähler zu ändern, können Sie anrufen Increment, Sie können den Wert sogar zurückerhalten, wenn Sie mit einem Nullwert erhöhen.

Angenommen, Sie müssen diesen Code testen. Wie setze ich den Zähler nach jedem Test zurück? Und wenn Sie Tests parallel ausführen möchten, wie kann dies durchgeführt werden? Angenommen, Sie möchten mehrere Zähler im Programm verwenden. Werden Sie Erfolg haben?

Natürlich nicht. Offensichtlich besteht die Lösung darin, die Variable variablein den Typ zu kapseln .

package counter

type Counter struct {
        count int
}

func (c *Counter) Increment(n int) int {
        c.count += n
        return c.count
}

Stellen Sie sich nun vor, dass das beschriebene Problem nicht auf Zähler beschränkt ist, sondern auch die Hauptgeschäftslogik Ihrer Anwendungen betrifft. Können Sie es isoliert testen? Können Sie parallel testen? Können Sie mehrere Instanzen gleichzeitig verwenden? Wenn die Antwort für alle Fragen Nein lautet, ist der Grund der Status auf Paketebene.

Vermeiden Sie diese Bedingungen. Reduzieren Sie die Konnektivität und die Anzahl der Albtraum-Remote-Aktionen, indem Sie den Typen die Abhängigkeiten bereitstellen, die sie als Felder benötigen, anstatt Paketvariablen zu verwenden.

Machen Sie Pläne für das Scheitern, nicht für den Erfolg


Übergeben Sie Fehler niemals lautlos.

Das Zen von Python, Eintrag 10

Dies gilt für Sprachen, die die Ausnahmebehandlung im Samurai-Stil fördern: Kommen Sie mit einem Sieg zurück oder kommen Sie überhaupt nicht zurück. In Sprachen, die auf Ausnahmen basieren, geben Funktionen nur gültige Ergebnisse zurück. Wenn die Funktion dies nicht kann, läuft der Regelungsfluss ganz anders ab.

Ungeprüfte Ausnahmen sind offensichtlich ein unsicheres Programmiermodell. Wie können Sie bei Fehlern zuverlässigen Code schreiben, wenn Sie nicht wissen, welche Ausdrücke eine Ausnahme auslösen können? Java versucht, Risiken mit dem Konzept der geprüften Ausnahmen zu reduzieren. Und soweit ich weiß, gibt es in anderen populären Sprachen keine Analoga dieser Lösung. Es gibt Ausnahmen in vielen Sprachen, und überall außer in Java werden sie nicht überprüft.

Offensichtlich ging Go einen anderen Weg. Go-Programmierer glauben, dass zuverlässige Programme aus Teilen bestehen, die Fehler behandeln, bevor erfolgreiche Pfade verarbeitet werden. Angesichts der Tatsache, dass die Sprache für die Serverentwicklung, die Erstellung von Multithread-Programmen sowie für Programme zur Verarbeitung von über das Netzwerk eingegebenen Daten erstellt wurde, sollten sich Programmierer auf die Arbeit mit unerwarteten und beschädigten Daten, Zeitüberschreitungen und Verbindungsfehlern konzentrieren. Natürlich, wenn sie zuverlässige Produkte herstellen wollen.

Ich glaube, dass Fehler explizit behandelt werden sollten, dies sollte der Hauptwert der Sprache sein.

Peter Burgon, GoTime # 91

Ich schließe mich den Worten von Peter an, sie dienten als Anstoß für das Schreiben dieses Artikels. Ich glaube, dass Go seinen Erfolg der expliziten Fehlerbehandlung verdankt. Programmierer denken hauptsächlich über mögliche Abstürze nach. Zunächst lösen wir Probleme wie „Was wäre wenn“. Das Ergebnis sind Programme, bei denen Fehler beim Schreiben von Code behandelt werden und nicht so, wie sie während des Betriebs auftreten.

Die Ausführlichkeit dieses Codes

if err != nil {
    return err
}

Überwiegt die Wichtigkeit, jeden fehlgeschlagenen Zustand zum Zeitpunkt seines Auftretens absichtlich zu behandeln. Der Schlüssel dazu ist der Wert der expliziten Behandlung jedes Fehlers.

Lieber früh zurückkehren als tief investieren


Geschwister sind besser als

das Zen von Python zu verschachteln , Eintrag 5

Dieser weise Rat kommt aus einer Sprache, in der Einrückungen die Hauptform des Kontrollflusses sind. Wie interpretieren wir diesen Tipp in der Go-Terminologie? gofmt verwaltet die gesamte Menge an leerem Speicherplatz in Go-Programmen, daher haben wir hier nichts zu tun.

Ich habe oben über Paketnamen geschrieben. Vielleicht ist es ratsam, eine komplexe Hierarchie von Paketen zu vermeiden. Nach meiner Erfahrung ist das Risiko eines zyklischen Imports von Paketen umso höher, je mehr ein Programmierer versucht, eine Codebasis auf Go zu trennen und zu klassifizieren.

Ich glaube, dass die beste Verwendung des fünften Eintrags aus The Zen of Python darin besteht, einen Kontrollfluss innerhalb einer Funktion zu erstellen . Vermeiden Sie mit anderen Worten einen Kontrollfluss, der eine mehrstufige Einrückung erfordert.

Direkte Sichtbarkeit ist eine gerade Linie, entlang der die Sicht durch nichts verdeckt wird.

May Ryer, Code: Richten Sie den glücklichen Pfad am linken Rand aus

May Ryer beschreibt diese Idee als Programmierung in direkter Sichtlinie:

  • Verwenden Sie Steueranweisungen, um frühzeitig zurückzukehren, wenn die Voraussetzung nicht erfüllt ist.
  • Platzieren der Anweisung für die erfolgreiche Rückgabe am Ende der Funktion und nicht innerhalb des bedingten Blocks.
  • Reduzieren Sie die Gesamtverschachtelungsebene, indem Sie Funktionen und Methoden extrahieren.

Stellen Sie sicher, dass wichtige Funktionen niemals außer Sichtweite zum rechten Bildschirmrand gelangen. Dieses Prinzip hat einen Nebeneffekt: Sie vermeiden bedeutungslose Streitigkeiten mit dem Team über die Länge der Linien.

Jedes Mal, wenn Sie einrücken, fügen Sie den Köpfen der Programmierer eine weitere Voraussetzung hinzu, die einen ihrer 7 ± 2 Kurzzeitspeicherplätze belegt. Versuchen Sie, den erfolgreichen Pfad der Funktion so nah wie möglich an der linken Seite des Bildschirms zu halten, anstatt die Verschachtelung zu vertiefen.

Wenn Sie glauben, dass etwas langsam läuft, beweisen Sie dies mit einem Benchmark


Geben Sie die Versuchung auf, angesichts von Zweideutigkeiten zu raten.

Das Zen von Python 12

Die Programmierung basiert auf Mathematik und Logik. Diese beiden Konzepte verwenden selten das Element Glück. Aber wir als Programmierer machen jeden Tag zahlreiche Annahmen. Was macht diese Variable? Was macht diese Option? Was passiert, wenn ich hier null passiere? Was passiert, wenn ich das Register zweimal anrufe? In der modernen Programmierung muss man viel annehmen, insbesondere wenn man die Bibliotheken anderer Leute benutzt.

Die API sollte einfach zu verwenden und schwer falsch zu verwenden sein.

Josh Bloch

Eine der besten Möglichkeiten, wie ich einem Programmierer helfen kann, beim Erstellen einer API keine Vermutungen anzustellen, besteht darin, sich auf Standardverwendungsmethoden zu konzentrieren . Der Anrufer sollte in der Lage sein, normale Vorgänge so einfach wie möglich auszuführen. Bevor ich jedoch viel geschrieben und über das Entwerfen der API gesprochen habe, ist hier meine Interpretation von Datensatz 12: Raten Sie nicht über das Thema Leistung .

Trotz Ihrer Einstellung zu Knuts Rat ist einer der Gründe für den Erfolg von Go die Effektivität seiner Ausführung. Effektive Programme können in dieser Sprache geschrieben werden, und dank dieses an, die Menschen werdenwähle go. Es gibt viele Missverständnisse in Bezug auf die Leistung. Wenn Sie nach Möglichkeiten suchen, die Codeleistung zu verbessern, oder dogmatische Tipps wie "Verlangsamung des Regals", "CGO ist teuer" oder "Verwenden Sie immer atomare Operationen anstelle von Mutexen" befolgen, sollten Sie keine Wahrsagerei betreiben.

Komplizieren Sie Ihren Code nicht aufgrund veralteter Dogmen. Und wenn Sie der Meinung sind, dass etwas langsam funktioniert, stellen Sie dies zunächst mithilfe eines Benchmarks sicher. Go bietet großartige kostenlose Benchmarking- und Profiling-Tools. Verwenden Sie sie, um Engpässe bei der Leistung Ihres Codes zu finden.

Bevor Sie mit Gorutin beginnen, müssen Sie herausfinden, wann es aufhören wird


Ich glaube, ich habe die wertvollen Gegenstände aus PEP-20 aufgelistet und ihre Interpretation vielleicht über den guten Geschmack hinaus erweitert. Das ist gut, denn obwohl dies ein nützliches rhetorisches Mittel ist, sprechen wir immer noch über zwei verschiedene Sprachen.

Schreiben Sie g, o, ein Leerzeichen und dann einen Funktionsaufruf. Drei Tastendrücke, es kann nicht kürzer sein. Mit drei Tastenklicks haben Sie den Unterprozess gestartet.

Rob Pike, Einfachheit ist kompliziert , dotGo 2015

Die nächsten beiden Tipps widme ich den Goroutinen. Gorutine sind ein charakteristisches Merkmal der Sprache, unsere Reaktion auf die Wettbewerbsfähigkeit auf hohem Niveau. Sie sind sehr einfach zu bedienen: Stellen Sie ein Wort govor den Operator und Sie führen die Funktion asynchron aus. Keine Ausführungsthreads, keine Pool-Executoren, keine IDs, keine Abschlussstatusverfolgung.

Gorutine sind billig. Aufgrund der Fähigkeit der Laufzeitumgebung, Goroutinen in einer kleinen Anzahl von Ausführungsthreads zu multiplexen (die Sie nicht verwalten müssen), können Sie problemlos Hunderttausende oder Millionen von Goroutinen erstellen. Auf diese Weise können Sie Architekturen erstellen, die bei Verwendung anderer Wettbewerbsmodelle in Form von Ausführungsthreads oder Ereignisrückrufen unpraktisch wären.

Aber egal wie billig die Goroutinen waren, sie sind nicht frei. Ihr Stapel benötigt mindestens einige Kilobyte. Und wenn Sie Millionen von Goroutinen haben, wird dies spürbar. Ich will damit nicht sagen, dass Sie nicht Millionen von Goroutinen verwenden müssen, wenn die Architektur Sie dazu drängt. Wenn Sie es jedoch verwenden, ist es äußerst wichtig, sie zu überwachen, da Goroutinen in solchen Mengen eine Menge Ressourcen verbrauchen können.

Goroutinen sind die Hauptbesitzquelle in Go. Um nützlich zu sein, muss Goroutine etwas tun. Das heißt, es enthält fast immer eine Verknüpfung zu einer Ressource, dh Eigentumsinformationen: Sperre, Netzwerkverbindung, Datenpuffer, der das Ende des Kanals sendet. Während die Goroutine lebt, wird die Sperre gehalten, die Verbindung bleibt offen, der Puffer wird gespeichert und die Kanalempfänger warten auf neue Daten.

Der einfachste Weg, Ressourcen freizugeben, besteht darin, sie mit dem Lebenszyklus der Goroutine zu verknüpfen. Wenn es abgeschlossen ist, werden Ressourcen freigegeben. Und da es sehr einfach ist, Goroutine auszuführen, stellen Sie vor dem Schreiben von "go and space" sicher, dass Sie Antworten auf die folgenden Fragen haben:

  • Unter welchen Bedingungen hört Goroutine auf? Go kann Goroutine nicht sagen, dass sie enden soll. Aus einem bestimmten Grund gibt es keine Funktion zum Stoppen oder Unterbrechen. Wir können nicht befehlen, dass die Goroutinen aufhören, aber wir können höflich fragen. Dies hängt fast immer mit dem Betrieb des Kanals zusammen. Wenn es geschlossen ist, wird der Bereich wiederholt, um den Kanal zu verlassen. Wenn Sie den Kanal schließen, können Sie ihn auswählen. Das Signal von einer Goroutine zur anderen wird am besten als geschlossener Kanal ausgedrückt.
  • ? , , : ?
  • , ? , - . , . . , .


Wahrscheinlich wird in einem Ihrer seriösen Go-Programme Parallelität verwendet. Dies führt häufig zu dem Problem eines Arbeitermusters - eine Goroutine pro Verbindung.

Ein Paradebeispiel ist net / http. Es ist ganz einfach, den Server zu stoppen, dem der Listening-Socket gehört, aber was ist mit den Goroutinen, die von diesem Socket generiert werden? net / http stellt ein Kontextobjekt innerhalb des Anforderungsobjekts bereit, mit dem dem Listener mitgeteilt werden kann, dass die Anforderung abgebrochen werden muss, und daher die Goroutine unterbrochen wird. Es ist jedoch nicht klar, wie man herausfinden kann, wann dies alles getan werden muss. Es ist eine Sache anzurufen context.Cancel, eine andere zu wissen, dass die Stornierung abgeschlossen ist.

Ich finde oft Fehler mit net / http, aber nicht, weil es schlecht ist. Im Gegenteil, es ist die erfolgreichste, älteste und beliebteste API in der Go-Codebasis. Daher werden seine Architektur, Entwicklung und Mängel sorgfältig analysiert. Betrachten Sie diese Schmeichelei, nicht Kritik.

Daher möchte ich net / http als Gegenbeispiel für bewährte Verfahren einbringen. Da jede Verbindung von dem innerhalb des Typs erstellten Goroutin verarbeitet wird net/http.Server, kann das Programm außerhalb des net / http-Pakets die vom empfangenden Socket erstellten Goroutins nicht steuern.

Dieser Bereich der Architektur entwickelt sich noch. Sie können run.Groupdas Go-Kit oder ErrGroup des Go-Entwicklungsteams aufrufen, das ein Framework zum Ausführen, Abbrechen und Warten auf asynchron ausgeführte Funktionen bietet.

Für alle, die Code schreiben, der asynchron ausgeführt werden kann, besteht das Hauptprinzip beim Erstellen von Architekturen darin, dass die Verantwortung für das Ausführen von Goroutinen auf den Aufrufer verlagert wird. Lassen Sie ihn auswählen, wie er ausgeführt werden soll, verfolgen und warten, bis Ihre Funktionen abgeschlossen sind.

Schreiben Sie Tests, um das Verhalten Ihrer Paket-API zu blockieren


Sie haben vielleicht gehofft, dass ich in diesem Artikel das Testen nicht erwähnen werde. Entschuldigung, ein andermal.

Ihre Tests sind eine Vereinbarung darüber, was Ihr Programm tut und was nicht. Unit-Tests sollten das Verhalten ihrer APIs auf Paketebene blockieren. Tests beschreiben in Codeform, was das Paket verspricht. Wenn es für jede Eingabekonvertierung einen Komponententest gibt, haben Sie in Form von Code und nicht in Form einer Dokumentation eine Vereinbarung über die Funktionsweise des Codes definiert.

Die Genehmigung dieser Vereinbarung ist so einfach wie das Schreiben eines Tests. Zu jedem Zeitpunkt können Sie angeben , mit einem hohen Maß an Vertrauen , dass das Verhalten , dass die Menschen verlassen , bevor die Änderungen vorgenommen Sie Funktion nach den Änderungen fortsetzen wird.

Testet das Block-API-Verhalten. Alle Änderungen, die die öffentliche API hinzufügen, ändern oder entfernen, sollten Änderungen in den Tests enthalten.

Mäßigung ist eine Tugend


Go ist eine einfache Sprache mit nur 25 Schlüsselwörtern. In gewisser Weise werden dadurch die in die Sprache integrierten Funktionen hervorgehoben. Dies sind die Merkmale, die es der Sprache ermöglichen, sich selbst zu fördern: einfacher Wettbewerb, strukturelle Typisierung usw.

Ich denke, wir alle sind verwirrt, wenn wir versuchen, alle Funktionen von Go gleichzeitig zu nutzen. Wie viele von Ihnen waren von der Verwendung von Kanälen so begeistert, dass Sie sie verwendet haben, wo immer Sie können? Ich fand heraus, dass die resultierenden Programme schwer zu testen, fragil und zu komplex sind. Und Sie?

Ich hatte die gleiche Erfahrung mit Goroutinen. Beim Versuch, das Werk in winzige Fragmente zu unterteilen, schuf ich die Dunkelheit von Goroutin, die schwer zu kontrollieren war, und verlor völlig die Tatsache aus den Augen, dass die meisten von ihnen immer blockiert waren, weil ihre Vorgänger erwarteten, das Werk fertigzustellen. Der Code war vollständig konsistent und ich musste die Komplexität erheblich erhöhen, um einen kleinen Vorteil zu erzielen. Wie viele von Ihnen sind darauf gestoßen?

Ich hatte das gleiche mit dem Einbetten. Zuerst habe ich es mit Vererbung verwechselt. Dann stieß er auf das Problem einer fragilen Basisklasse, bei der mehrere komplexe Typen, die bereits mehrere Aufgaben hatten, zu noch komplexeren großen Typen kombiniert wurden.

Dies mag der am wenigsten wirksame Rat sein, aber ich halte es für wichtig, ihn zu erwähnen. Der Rat ist der gleiche: Halten Sie die Moderation aufrecht, und die Fähigkeiten von Go sind keine Ausnahme. Verwenden Sie nach Möglichkeit keine Goroutinen, Kanäle, Einbettungsstrukturen, anonymen Funktionen, eine Fülle von Paketen und Schnittstellen. Verwenden Sie einfachere Lösungen als intelligente.

Einfache Wartung ist wichtig


Zum Schluss gebe ich Ihnen noch einen Eintrag aus PEP-20:

Lesbarkeit ist wichtig.

Das Zen von Python, Rekord 7

Es wurde viel über die Bedeutung der Lesbarkeit von Code in allen Programmiersprachen gesagt. Diejenigen, die für Go werben, verwenden Wörter wie Einfachheit, Lesbarkeit, Klarheit und Produktivität. Aber all dies sind Synonyme für ein Konzept - die Bequemlichkeit der Wartung.

Das eigentliche Ziel besteht darin, Code zu erstellen, der einfach zu warten ist. Der Code, der den Autor überlebt. Ein Code, der nicht nur als Zeitinvestition, sondern auch als Grundlage für die Erlangung des zukünftigen Werts existieren kann. Dies bedeutet nicht , dass die Lesbarkeit ist nicht wichtig, nur die Bequemlichkeit der Wartung ist wichtiger .

Go ist keine dieser Sprachen, die für einzeilige Programme optimiert sind. Und keine dieser Sprachen, die für Programme mit einer minimalen Anzahl von Zeilen optimiert sind. Wir optimieren nicht für die Größe des Quellcodes auf der Festplatte oder für die Geschwindigkeit, mit der Programme im Editor geschrieben werden. Wir möchten unseren Code optimieren, damit er für die Leser verständlicher wird. Weil sie ihn begleiten müssen.

Wenn Sie ein Programm für sich selbst schreiben, wird es möglicherweise nur einmal gestartet, oder Sie sind der einzige, der seinen Code sieht. In diesem Fall tun Sie alles. Wenn jedoch mehr als eine Person an dem Code arbeitet oder wenn er längere Zeit verwendet wird und sich die Anforderungen, Funktionen oder die Laufzeit ändern können, sollte das Programm bequem zu warten sein. Wenn die Software nicht gewartet werden kann, kann sie nicht neu geschrieben werden. Und dies ist möglicherweise das letzte Mal, dass Ihr Unternehmen in Go investiert.

Woran Sie hart arbeiten, können Sie nach Ihrer Abreise bequem begleiten? Wie können Sie die Pflege Ihres Codes für diejenigen erleichtern, die heute nach Ihnen kommen?

All Articles