Go Home Page
Die Programmiersprache Go

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:

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:

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:

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

[Original] von Sameer Ajmani