The Go Blog — Package names — Deutsche Übersetzung
- Das Original:
-
https://blog.golang.org/package-names
February 4, 2015 - Diese Übersetzung:
-
http://www.bitloeffel.de/DOC/golang/package-names_de.html
Stand: 08.05.2015
© 2015 Hans-Werner Heinzen @ Bitloeffel.de
Die Nutzung dieses Werks ist unter den Bedingungen der "Creative Commons Attribution 3.0"-Lizenz erlaubt.
Go Blog: Paketnamen
4. Februar 2015
Einführung
Go-Kode wird in Paketen organisiert.
Innerhalb eines Pakets kann der Kode sich auf jeden dort definierten Bezeichner
(Namen) beziehen, während Benutzer des Pakets nur auf exportierte
Typen, Funktionen, Konstanten oder Variablen verweisen können. So ein Verweis
enthält den Paketnamen als Präfix: foo.Bar
verweist auf den
exportierten Namen Bar
im importierten Paket namens foo
.
Gute Paketnamen führen zu besserem Kode. Der Name eines Pakets liefert Kontext für seinen Inhalt, und hilft dem Benutzer zu verstehen, wie und wofür ein Paket zu benutzen ist. Bei der Weiterentwicklung des Pakets hilft der Name den Entwicklern zu erkennen, was oder was nicht ins Paket hineingehört. Pakete mit gut gewählten Namen erleichtern zu finden, was gerade gebraucht wird.
"Effective Go" nennt Richtlinien [de] für das Benennen von Paketen, Typen, Funktionen und Variablen. Der vorliegende Aufsatz vertieft diese Diskussion und untersucht Namen aus der Standardbibliothek. Er diskutiert auch schlechte Paketnamen, und wie man sie korrigiert.
Paketnamen
Gute Paketnamen sind kurz und klar. Sie werden klein geschrieben und enthalten
weder unter_striche
noch binnenMajuskeln
.
Es sind oft einfache Substantive:
-
time
(bietet Funktionalität zum Messen und Anzeigen von Zeit) -
list
(implementiert eine doppelt verkettete Liste) -
http
(stellt Implementierungen für HTTP-Klienten und -Server bereit)
Der typische Namensstil einer anderen Sprache kann für Go unpassend sein. Hier zwei Beispielnamen, die in anderen Sprachen guter Stil sein mögen, aber schlecht zu Go passen:
-
computeServiceClient
-
priority_queue
Ein Go-Paket kann verschiedenste Typen und Funktionen exportieren. Zu Beispiel
könnte ein Paket compute
einen Typ Client
plus Methoden
ebenso exportieren wie Funktionen, die Compute-Aufgaben an verschiedene
Klienten verteilt.
Kürzen Sie sinnvoll ab. Paketnamen können abgekürzt werden, wenn die Abkürzung dem Programmierer vertraut ist. Weit verbreitete Pakete haben oft komprimierte Namen:
-
strconv
(Zeichenkettenkonvertierungen) -
syscall
(Systemaufrufe) -
fmt
(formatierte Ein-/Ausgabe)
Andererseits, wenn durchs Abkürzen ein Paketname zweideutig oder unklar würde: Lassen Sie's.
Nehmen Sie den Benutzern keine gute Namen weg.
Vermeiden Sie Paketnamen,
die in Kode gerne anders genutzt werden. Beispielsweise wurde das Paket
für gepufferte Ein-/Ausgabe bufio
und nicht buf
genannt,
weil buf
auch ein guter Variablenname für einen Puffer ist.
Paketinhalte benennen
Ein Paketname und die Namen seiner Inhalte hängen zusammen, weil Benutzer sie zusammen benutzen. Wenn Sie ein Paket planen, nehmen Sie den Standpunkt des Benutzers ein.
Stottern Sie nicht.
Weil Benutzer den Paketnamen als Präfix benutzen, um
auf Paketinhalte zu verweisen, brauchen die Inhaltsnamen den Paketnamen nicht
zu wiederholen. Der HTTP-Server, der vom Paket http
bereitgestellt
wird, heißt Server
, nicht httpServer
. Benutzerkode
spricht diesen mit http.Server
an; das ist eindeutig.
Vereinfachen Sie Funktionsnamen.
Wenn eine Funktion im Paket pkg
einen Wert vom Typ pkg.Pkg
(oder *pkg.Pkg
) zurückgibt,
dann kann der Funktionsname oft genug auf den Typnamen verzichten, ohne
unverständlich zu werden:
start := time.Now() // start ist vom Typ time.Time t, err := time.Parse(time.Kitchen, "6:06PM") // t ist vom Typ time.Time ctx = context.WithTimeout(ctx, 10*time.Millisecond) // ctx ist vom Typ context.Context ip, ok := userip.FromContext(ctx) // ip ist vom Typ net.IP
Eine Funktion namens New
im Paket pkg
gibt einen Wert
vom Typ pkg.Pkg
zurück:
q := list.New() // q ist vom Typ *list.List
Wenn eine Funktion einen Wert vom Typ pkg.T
zurückgibt, und
T
nicht Pkg
ist, dann mag T
Teil des
Funktionsnamens werden, um dem Benutzer das Verständnis zu erleichtern.
So etwas ist üblich in Paketen, die mehrere Funktionen der Art "New" anbieten:
d, err := time.ParseDuration("10s") // d ist vom Typ time.Duration elapsed := time.Since(start) // elapsed ist vom Typ time.Duration ticker := time.NewTicker(d) // ticker ist vom Typ *time.Ticker timer := time.NewTimer(d) // timer ist vom Typ *time.Timer
Typen in verschiedenen Paketen dürfen gleiche Namen haben, weil aus dem
Blickwinkel des Benutzers sich solche Namen durch die Paketnamen unterscheiden.
Beispielsweise bietet die Standardbibliothek verschiedene Typen mit dem Namen
Reader
an, so jpeg.Reader
,
bufio.Reader
und csv.Reader
.
Jeder dieser Paketnamen ergibt zusammen mit Reader
einen guten
Typnamen.
Wenn Ihnen kein Paketname einfällt, der auch ein sinnvoller Präfix für die Paketinhalte wäre, dann liegt ihrem Paket vielleicht eine ungeeignete Abstraktion zu Grunde. Schreiben Sie dann Kode, wie es ein Paketnutzer tun würde, und, wenn das dann bescheiden aussieht, restrukturieren Sie Ihr Paket. Auf diese Weise entstehen Pakete, die von Benutzern leichter zu verstehen und von Paketentwicklern leichter zu pflegen sind.
Pakete und Pfade
Zu einem Go-Paket gehört ein Name und ein Pfadname.
Der Paketname wird durch die package
-Anweisung seiner Quelldateien
festgelegt; Benutzerkode gebraucht ihn als Präfix für die exportierten Namen
des Pakets. Benutzerkode gebraucht den Pfadnamen, um das Paket zu importieren.
Es ist Konvention, dass die letzte Komponente des Pfadnamens auch der Paketname ist:
import ( "fmt" // Paket fmt "os/exec" // Paket exec "golang.org/x/net/context // Paket context )
Umwandlungswerkzeuge [englisch: build tools, A.d.Ü.] ordnen den Pfadnamen
Verzeichnisnamen zu. Das go
-Kommando benutzt die Umgebungsvariable
GOPATH
[de],
um die Quelldateien für den Pfad "github.com/user/hello"
im Verzeichnis $GOPATH/src/github.com/user/hello
zu finden.
(Das ist sicher alles schon bekannt, aber mir ist wichtig, dass die Terminologie
und die Struktur von Paketen ganz klar sind.)
Verzeichnisse.
Die Standardbibliothek benutzt Verzeichnisse wie
crypto
, container
, encoding
und
image
, um Pakete für verwandte Protokolle oder Algorithmen zu
gruppieren. Es gibt keine direkten Verbindungen zwischen den Paketen in einem
dieser Verzeichnisse; das Verzeichnis dient nur zum Ordnen der Dateien.
Jedes Paket kann jedes andere Paket importieren, vorausgesetzt die Imports
bilden keine Schleife.
Genauso wie Typen in verschiedenen Paketen den gleichen Namen haben dürfen,
ohne mehrdeutig zu werden, so dürfen Pakete in verschiedenen Verzeichnissen
den gleichen Namen tragen.
Zu Beispiel stellt runtime/pprof
Daten für die Laufzeitanalyse zur Verfügung, in einem Format, wie es das
Analysewerkzeug pprof
erwartet. Hingegen liefert
net/http/pprof HTTP-Endpunkte,
die als solche präsentiert werden können.
Benutzerkode importiert mittels Paketpfad, so dass keine Verwechslungsgefahr
besteht. Muss eine Quelldatei tatsächlich einmal beide pprof
-Pakete
importieren, so kann sie eines oder beide Pakete lokal
umbenennen
[de].
Der lokale Name sollte denselben Richtlinien folgen wie ein Paketname;
also: Kleinbuchstaben, keine unter_striche
,
keine binnenMajuskeln
.
Schlechte Paketnamen
Schlechte Paketnamen machen Kode schwerer zu durchschauen und schwieriger zu pflegen. Hier ein paar Hilfen zum Erkennen und Korrigieren schlechter Paketnamen.
Vermeiden Sie bedeutungslose Namen.
Pakete mit Namen wie util
, common
oder misc
geben dem Benutzer keinen Hinweis darauf, was das Paket enthält. Das macht dem Benutzer
das Benutzen schwerer, und dem Entwickler erschwert es, das Paket fokussiert zu
halten. Im Laufe der Zeit wird so ein Paket Abhängigkeiten ansammeln, wodurch
das Umwandeln unnötig verlangsamt wird, besonders in großen Programmen.
Und weil diese Paketnamen so allgemein gehalten sind, ist die Wahrscheinlichkeit
einer Namenskollision mit anderen importierten Paketen größer, wodurch man wieder
gezwungen wird, zur Unterscheidung sich lokale Namen auszudenken.
Spalten Sie "Allgemeinpakete" auf.
Zum Reparieren solcher Pakete suchen Sie nach gemeinsamen Namensbestandteilen
und ziehen den entsprechenden Kode in ein eigenes Paket heraus.
Sie haben zum Beispiel ein Paket util
mit:
package util func NewStringSet(...string) map[string]bool {...} func SortStringSet(map[string]bool) []string {...}
Dann sieht's im Benutzerkode vielleicht so aus:
set := util.NewStringSet("c", "a", "b") fmt.Println(util.SortStringSet(set))
Ziehen Sie nun diese Funktionen aus dem Paket util
heraus in ein
neues Paket, und wählen Sie einen Namen der zum Inhalt passt:
package stringset func New(...string) map[string]bool {...} func Sort(map[string]bool) []string {...}
Dann wird der Benutzerkode zu:
set := stringset.New("c", "a", "b") fmt.Println(stringset.Sort(set))
Nach dieser Änderung fällt es leicht zu sehen, wie das Paket weiter verbessert werden kann.
package stringset type Set map[string]bool func New(...string) Set {...} func (s Set) Sort() []string {...}
Was zu noch kürzerem Benutzerkode führt:
set := stringset.New("c", "a", "b") fmt.Println(set.Sort())
Der Paketname ist ein entscheidendes Gestaltungselement. Machen Sie sich die Mühe, und eliminieren Sie bedeutungslose Paketnamen aus Ihren Projekten.
Benutzen Sie nicht ein Paket für alle Ihre Schnittstellen.
Viele gutmeinende Programmierer stecken alle ihre öffentlichen Schnittstellen
in ein einziges Paket mit Namen api
, types
oder interfaces
, und meinen, damit findet man die Einstiegspunkte
in den Kode besser.
Das ist ein Irrtum.
Solche Pakete leiden unter den gleichen Problemen wie die mit Namen
util
oder common
, weil sie grenzenlos wachsen, dem
Benutzer keine Hilfestellung geben, weil sie Abhängigkeiten ansammeln und mit
anderen Imports kollidieren.
Spalten Sie sie auf, eventuell in verschiedene Verzeichnisse, um öffentliche
Pakete von Implementierungen zu trennen.
Vermeiden Sie unnötige Namenskollisionen.
Wenn auch Pakete in verschiedenen Verzeichnissen den gleichen Namen haben dürfen,
so sollten doch Pakete, die oft zusammen verwendet werden, verschiedene Namen haben.
Das vermeidet Konfusion und den Bedarf an neuen lokalen Namen im Benutzerkode.
Vermeiden Sie aus dem gleichen Grund Namen, die es für populären Standardpaketen
schon gibt, etwa io
oder http
.
Fazit
Paketnamen sind das Herzstück einer guten Namenswahl in Go-Programmen. Nehmen Sie sich die Zeit, gute Paketnamen zu wählen und den Kode sinnvoll zu strukturieren. Das hilft den Benutzern beim Verstehen und den Paketpflegern, das Paket elegant weiterzuentwickeln.
Weiterführendes
- "Effective Go" ["Effektiv Go programmieren"]
- "How to Write Go Code" ["Wie man mit Go arbeitet"]
- "Organizing Go Code (2012 blog post)"
- "Organizing Go Code (2014 Google I/O talk)"
[Original] von Sameer Ajmani