Tutorial: Create a Go Module — Deutsche Übersetzung
- Das Original:
-
https://golang.org/doc/tutorial/create-module
Version of March 3, 2021 - Diese Übersetzung:
-
https://bitloeffel.de/DOC/golang/create-module_20210817_de.html
Stand: 04.08.2021
© 2021 Hans-Werner Heinzen @ Bitloeffel.de
Die Nutzung dieses Werks ist unter den Bedingungen der "Creative Commons Attribution 3.0"-Lizenz erlaubt.
Für Fachbegriffe und ähnliches gibt es hier noch eine Wörterliste.
Übung: Module erstellen
Im Verlauf dieser Übung werden Sie zwei Module erstellen. Erstens eine Bibliothek, die von anderen Bibliotheken oder Programmen importiert werden kann. Zweitens ein Programm, welches das erste benutzt.
Diese Übung behandelt sieben Themen (in sieben Lektionen), die jeweils andere Aspekte der Sprache beleuchten.
- Erstellen eines Moduls — Schreiben Sie ein kleines Modul mit Funktionen, die Sie von einem anderen Modul aufrufen können.
- Rufen aus einem anderen Modul heraus — Importieren und benutzen Sie Ihr neues Modul.
- Rückgabe und Verarbeiten eines Fehlers — Fügen Sie eine einfache Fehlerbehandlung hinzu.
- Rückgabe einer zufälligen Grußformel — Verarbeiten Sie Daten mit Slices (das sind Arrays dynamischer Größe in Go).
- Rückgabe von Grußformeln für mehrere Adressaten — Speichern Sie Schlüssel/Wert-Paare in einer Map.
- Erstellen eines Tests — Benutzen Sie Go's Standardverfahren für Komponententests.
- Fertigen und Installieren der Anwendung — Fertigen und Installieren Sie Ihren Kode lokal.
- Zusammenfassung — Noch ein paar Hinweise zu Go-Modulen.
Hinweis: Weitere Übungen finden Sie hier.
Voraussetzungen
- Etwas Programmiererfahrung. Der Kode hier ist recht einfach, aber es hilft, wenn man etwas über Funktionen, Schleifen und Arrays weiß.
- Ein Werkzeug zum Bearbeiten Ihres Kodes. Ein beliebiger Texteditor genügt; die meisten unterstützen das Arbeiten mit Go. Beliebt sind VSCode (frei), GoLand (kostet etwas) und Vim (frei).
- Ein Kommandofenster. Go arbeitet unter Linux oder auf dem Mac in jedem Terminal, und unter Windows in der PowerShell oder im cmd-Fenster.
1. Erstellen eines Moduls, das andere nutzen können
Beginnen wir mit dem Erstellen eines Go-Moduls. Ein Modul versammelt ein oder mehrere zusammengehörige Pakete, die einen eigenständigen, nützlichen Funktionssatz bilden. Zum Beispiel könnten Sie ein Modul mit Paketen zur Finanzanalyse erstellen, so dass andere, die eine Finanzanwendung schreiben, Ihre Vorarbeit nutzen können. Mehr zum Entwickeln von Modulen finden Sie in "Developing and publishing modules".
Go-Kode wird in Paketen gesammelt, und Pakete in Modulen. Ihr Modul benennt Abhängigkeiten Ihres Kodes, die benutzte Go-Version und die Menge der benötigten anderen Module eingeschlossen.
Im Laufe der Zeit ergänzen und verbessern Sie die Funktionalität Ihres Moduls und veröffentlichen immer neue Versionen. Entwickler, deren Kode Funktionen Ihres Moduls aufruft, können vor einer Produktionsübergabe das aktualisierte Paket importieren und mit der neuen Version testen.
-
In einer Kommandozeile begeben Sie sich in ihr Home-Verzeichnis.
Unter Linux oder auf dem Mac mit:
$ cd
Unter Windows mit:
cd %HOMEPATH%
-
Erstellen Sie für die Go-Modulquellen einen Ordner namens
greetings
.Das tun Sie zum Beispiel aus Ihrem Home-Verzeichnis heraus mit den folgenden Kommandos:
$ mkdir greetings $ cd greetings
-
Initialisieren Sie Ihr Modul mit dem Kommando
go mod init
, um die Dateigo.mod
zu erzeugen.Rufen Sie das Kommando
go mod init
und geben Sie den Modulpfad mit — in unserem Beispiel verwenden wirexample.com/greetings
. Wenn Sie Ihr Modul veröffentlichen, dann ist das die Adresse, von der Ihr Modul durch die Go-Tools kopiert werden kann (download), also die Ihres Quellkode-Depots.$ go mod init example.com/greetings go: creating new go.mod: module example.com/greetings
Das Kommando
go mod init
erzeugt eine Dateigo.mod
, die die Abhängigkeiten Ihres Moduls überwacht. Jetzt gerade enthält die Datei nur den Namen Ihres Moduls und die Go-Version, mit der Ihr Kode funktioniert. Doch für jede Abhängigkeit, die Sie hinzufügen, wird die Dateigo.mod
das zugehörige Modul mit seiner Version festhalten. Damit bleiben Umwandlungen reproduzierbar, und Sie haben unmittelbare Kontrolle darüber, welche Modulversionen benutzt werden. -
Erzeugen Sie mit einem Texteditor eine Datei für Ihren Kode
und nennen sie
greetings.go
. -
Fügen Sie dort den folgenden Kode ein, und sichern sie die Datei.
package greetings import "fmt" // Hello gibt einen Gruß an die genannte Person zurück. func Hello(name string) string { // Rückgabe eines Grußtexts mit eingefügtem Namen. message := fmt.Sprintf("Hi %v. Wie geht's.", name) return message }
Das ist der erste Kode für ihr Modul. Er gibt jedem Rufer, der danach fragt, einen Gruß zurück. In der nächsten Lektion werden wir dann Kode schreiben, der diese Funktion aufruft.
Im obigen Kode tun Sie folgendes:
-
Sie deklarieren ein Paket
greetings
, um darin verwandte Funktionen zu sammeln. -
Sie implementieren eine Funktion
Hello
, die einen Gruß zurückgibt.Diese Funktion nimmt einen Parameter
name
vom Typstring
entgegen. Sie gibt einenstring
zurück. In Go kann eine Funktion, deren Name mit einem Großbuchstaben beginnt, von einer Funktion außerhalb des Pakets aufgerufen werden. Das ist in Go als exportierter Name bekannt. Mehr über exportierte Namen finden Sie unter "Exported names" in der Go-Tour. -
Sie deklarieren eine Variable
message
die den Gruß aufnimmt.In Go ist der Operator
:=
eine Abkürzung zum Deklarieren und Initialisieren einer Variablen in einer Zeile. (Go erkennt am Wert auf der rechten Seite den Typ der Variablen.) In Langversion hätten Sie auch schreiben können:var message string message = fmt.Sprintf("Hi %v. Wie geht's.", name)
-
Sie benutzen die Funktion
Sprintf
aus dem Paketfmt
, um eine Grußnachricht zu erzeugen. Beim ersten Argument handelt es sich um einen Formatstring;Sprintf
ersetzt dort den Platzhalter%v
mit dem Wert des Parametersname
. Damit ist der Grußtext vollständig. - Sie geben den formatierten Grußtext an den Rufer zurück.
-
Sie deklarieren ein Paket
Im nächsten Schritt werden Sie diese Funktion von einem anderen Modul aus aufrufen.
2. Rufen aus einem anderen Modul heraus
In dieser Lektion werden wir Kode schreiben, der die
Hello
-Funktion des gerade erstellten Moduls aufruft.
Unser Kode wird als Programm ausführbar sein und Kode des
greetings
-Moduls nutzen.
-
Erstellen Sie für die Go-Modulquellen einen Ordner namens
hello
. Dorthin schreiben Sie Ihren Rufer.Nachdem Sie diesen Ordner angelegt haben, sollten Sie sowohl einen
hello
- als auch einengreetings
-Ordner auf derselben Hierarchiestufe haben:<home>/ |-- greetings/ |-- hello/
Wenn Sie sich zum Beispiel gerade im
greetings
-Ordner befinden, können Sie das mit folgenden Kommandos tun:$ cd .. $ mkdir hello $ cd hello
-
Aktivieren Sie die Überwachung der Abhängigkeiten für den
neuen Kode.
Dazu führen Sie das
go mod init
-Kommando aus, mit dem Modulnamen, zu dem Ihr Kode gehören soll.In unserem Beispiel verwenden wir
example.com/hello
als Modulpfad.$ go mod init example.com/hello go: creating new go.mod: module example.com/hello
-
Erzeugen Sie mit einem Texteditor im
hello
-Ordner eine Datei für Ihren Kode und nennen Sie siehello.go
. -
Schreiben Sie Kode, mit dem Sie unsere
Hello
-Funktion aufrufen und geben Sie den zurückgegebenen Wert aus.Dazu können Sie in hello.go den folgendes einfügen:
package main import ( "fmt" "example.com/greetings" ) func main() { // Gruß besorgen und ausgeben. message := greetings.Hello("Rosa") fmt.Println(message) }
Im obigen Kode tun Sie folgendes:
-
Sie deklarieren ein
main
-Paket. Als Programm ausführbarer Kode muss sich bei Go immain
-Paket befinden. -
Sie importieren zwei Pakete,
example.com/greetings
und dasfmt
-Paket. Damit bekommen Sie Zugriff auf (exportierte) Funktionen dieser Pakete. Indem Sieexample.com/greetings
importieren — das Paket, das sie vorhin erstellt haben — bekommen Sie Zugriff auf die FunktionHello
. Außerdem importieren Siefmt
, mit seinen Funktionen zur Ein- und Ausgabe von Text (wie etwa Ausgeben von Text auf die Konsole). -
Sie kommen an einen Grußformel, indem Sie die
Hello
-Funktion des Paketsgreetings
aufrufen.
-
Sie deklarieren ein
-
Bearbeiten Sie das
example.com/hello
-Modul so, dass es dasexample.com/greetings
-Modul benutzt.Für eine "echte" Anwendung würden Sie das
example.com/greetings
-Modul aus einem Quellkode-Depot (repository) veröffentlichen, wo Go-Tools es finden und kopieren könnten; der Modulpfad würde auf die öffentliche Adresse zeigen. Jetzt, da Sie das Modul nicht veröffentlicht haben, müssen Sie noch dasexample.com/hello
-Modul anpassen, damit es denexample.com/greetings
auf Ihrem lokalen Dateisystem findet.Führen Sie zum Bearbeiten des
example.com/hello
-Moduls das Kommandogo mod edit
aus, um die Go-Tools vom Modulpfad (wo das Modul NICHT ist) zu einem lokalen Ordner (wo es ist) umzuleiten.-
Führen Sie dazu auf der Kommandozeile aus dem Ordner
hello
folgendes Kommando aus:$ go mod edit -replace example.com/greetings=../greetings
Das Kommando legt fest, dass für das Verorten der Abhängigkeit
example.com/greetings
durch../greetings
ersetzt werden soll. Nach Ausführung des Kommandos sollte diego.mod
-Datei imhello
-Ordner einereplace
-Directive enthalten:module example.com/hello go 1.16 replace example.com/greetings => ../greetings
-
Zum Synchronisieren der Modulabhängigkeiten,
die vom Kode verlangt werden aber noch nicht im
Modul überwacht werden, führen
Sie nun auf der Kommandozeile das
go mod tidy
-Kommando aus:$ go mod tidy go: found example.com/greetings in example.com/greetings v0.0.0-00010101000000-000000000000
Danach sollte die
go.mod
-Datei des Modulsexample.com/hello
so aussehen:module example.com/hello go 1.16 replace example.com/greetings => ../greetings require example.com/greetings v0.0.0-00010101000000-000000000000
Das Kommando fand den lokalen Kode im
greetings
-Ordner, fügte einerequire
-Directive hinzu, um festzulegen, dassexample.com/hello
nachexample.com/greetings
verlangt; diese Abhängigkeit wurde durch den Import desgreetings
-Pakets inhello.go
erzeugt.Die Nummer hinter dem Modulpfad ist eine Pseudo-Versionsnummer — eine generierte Nummer anstelle der semantischen Versionsnummer (die das Modul noch nicht hat).
Um auf ein veröffentlichtes Modul zu verweisen, würde eine
go.mod
-Datei üblicherweise auf diereplace
-Directive verzichten und einerequire
-Directive mit echter Versionsnummer am Ende benutzen.require example.com/greetings v1.1.0
Mehr über Versionnummern erfahren Sie in "Module version numbering".
-
Führen Sie dazu auf der Kommandozeile aus dem Ordner
-
Um zu sehen, ob der Kode funktioniert starten Sie ihn im
hello
-Ordner.$ go run . Hi Rosa. Wie geht's.
Glückwunsch! Sie haben gerade zwei funktionierende Module geschrieben.
Im nächsten Schritt werden Sie eine Fehlerbearbeitung ergänzen.
3. Rückgabe und Verarbeiten eines Fehlers
Handhaben von Fehlern ist für zuverlässigen Kode unverzichtbar. In dieser Lektion ergänzen Sie Kode, um einen Fehler aus dem greetings-Modul zurückzugeben und diesen dann im rufenden Programm zu verarbeiten.
-
Ergänzen Sie
greetings/greetings.go
um den weiter unten hervorgehobenen Kode.Es macht wenig Sinn, einen Gruß zurückzugeben, wenn nicht bekannt ist, wer gegrüßt werden soll. Geben Sie also einen Fehler an den Rufer zurück, wenn
name
leer ist. Kopieren Sie die entsprechenden Zeilen nachgreetings.go
und speichen die Datei.package greetings import ( "errors" "fmt" ) // Hello gibt einen Gruß an die genannte Person zurück. func Hello(name string) (string, error) { // Wenn kein Name, dann Fehler mit Fehlermeldung zurückgeben. if name == "" { return "", errors.New("Name leer") } // Wenn mit Name, dann // Rückgabe eines Grußtexts mit eingefügtem Namen. message := fmt.Sprintf("Hi %v. Wie geht's.", name) return message, nil }
Im obigen Kode tun Sie folgendes:
-
Sie Ändern die Funktion so, dass sie zwei Werte zurückgibt:
einen
string
und einerror
. Ihr Rufer wird den zweiten Wert prüfen und daran sehen, ob ein Fehler aufgetreten ist. (Funktionen in Go können mehrere Werte zurückgeben. Mehr dazu in "Effective Go" (de).) -
Sie importieren aus der Go-Standardbibliothek das Paket
errors
, um daraus die Funktionerrors.New
benutzen zu können. -
Sie fügen eine
if
-Anweisung hinzu, um auf eine ungültige Anfrage hin zu prüfen (ein leerer String, wo ein Name sein sollte), und in diesem Fall einen Fehler zurückzugeben. Die Funktionerrors.New
liefert einenerror
, der Ihre Fehlernachricht enthält. -
Für den Erfolgsfall ergänzen Sie
nil
(was heißen soll: kein Fehler) in derreturn
-Anweisung. Daran kann der Rufer erkennen, dass die Funktion erfolgreich war.
-
Sie Ändern die Funktion so, dass sie zwei Werte zurückgibt:
einen
-
Verarbeiten Sie in der Datei
hello/hello.go
denerror
-Wert, der nun von der FunktionHello
zusätzlich zu dem bisherigen Wert zurückgegeben wird.Ergänzen Sie
hello.go
um den hier hervorgehobenen Kode:package main import ( "fmt" "log" "example.com/greetings" ) func main() { // Festlegen der Eigenschaften des Standard-Loggers: // ein Präfix für den Log-Eintrag und ein Schalter, der die // Ausgabe von Zeit, Quelldateiname und Zeilennummer unterbindet. log.SetPrefix("greetings: ") log.SetFlags(0) // Anfordern einer Grußnachricht message, err := greetings.Hello("") // Einen zurückgelieferter Fehler auf der Konsole ausgeben, // dann das Programm verlassen. if err != nil { log.Fatal(err) } // Wenn ohne Fehler, Ausgabe der Nachricht auf der Konsole. fmt.Println(message) }
Im obigen Kode tun Sie folgendes:
-
Sie richten das
log
-Paket so ein, dass am Beginn der Protokollnachricht der Kommandoname ("greetings"), aber keine Zeitangabe und keine Quelldateiinfos ausgegeben werden. -
Sie weisen beide Rückgabewerte von
Hello
Variablen zu, also auch denerror
-Wert. -
Sie vergessen Rosas Namen und geben als Argument für
Hello
einen leeren Sring mit, um so Ihre Fehlerverarbeitung testen zu können. -
Sie fragen nach einem Nicht-nil
error
-Wert. In diesem Fall ist weitermachen sinnlos. -
Sie nutzen Funktionen des
log
-Pakets aus der Standardbibliothek, um Ihre Fehlerinformation auszugeben. Tritt ein Fehler auf, so rufen Sie dessen FunktionFatal
zur Ausgabe und zum Abbrechen des Programms.
-
Sie richten das
-
Um zu bestätigen, dass der Kode funktioniert, starten Sie im
hello
-Ordner diehello.go
-Datei mitgo run
.Da Sie einen leeren Namen mitgeben, tritt jetzt ein Fehler auf.
$ go run . greetings: Name leer exit status 1
Das ist die übliche Fehlerbehandlung in Go: Geben Sie dem Rufer einen Wert zurück, damit er ihn prüfen kann.
Im nächsten Schritt werden Sie ein Go-typisches Slice benutzen, um einen zufällig gewählten Gruß zurückzugeben.
4. Rückgabe einer zufälligen Grußformel
In dieser Lektion ändern Sie den Kode so, dass er anstatt immer mit der gleichen Grußformel zu antworten eine von mehreren vorgefertigten Grußformeln zurückgibt.
Zu diesem Zweck benutzen Sie ein Slice. Ein Slice ist einem Array ähnlich, nur dass seine Größe sich dynamisch ändert, wenn Sie Elemente hinzufügen oder entfernen. Es ist einer der nützlichsten Typen in Go.
Sie werden nun ein kleines Slice mit drei Grußformeln einfügen, und lassen Ihren Kode eine von diesen zufällig wählen und zurückgeben. Mehr über Slices erfahren Sie im Blog-Beitrag "Go slices".
-
Ändern Sie
greetings/greetings.go
so, dass Ihr Kode wie dieser hier ausschaut:package greetings import ( "errors" "fmt" "math/rand" "time" ) // Hello gibt einen Gruß an die genannte Person zurück. func Hello(name string) (string, error) { // Wenn kein Name, dann Fehler mit Fehlermeldung zurückgeben. if name == "" { return "", errors.New("Name leer") } // Erzeugen eines Grußes in zufälliger ausgewählter Form. message := fmt.Sprintf(randomFormat(), name) return message, nil } // init versorgt Anfangswerte von Variablen, // die später in Funktionen benutzt werden. func init() { rand.Seed(time.Now().UnixNano()) } // randomFormat gibt aus einer Menge von Grußformeln // eine zurück; sie wird zufällig ausgewählt. func randomFormat() string { // Ein Slice von Formatstrings mit Grußformel. formats := []string{ "Hi %v. Wie geht's.", "Schön dich zu sehen, %v.", "%v! Habe die Ehre!", } // Zufällige Wahl einer Grußformel mithilfe eines // zufällig ermittelten Index für das Slice. return formats[rand.Intn(len(formats))] }
In diesem Kode tun Sie folgendes:
-
Sie fügen eine Funktion
randomFormat
hinzu, die ein zufällig ausgewähltes Grußformat zurückgibt. Beachten Sie, dassrandomFormat
mit einem Kleinbuchstaben beginnt, dass es also nur von Kode im selben Paket erreichbar ist; anders gesagt,randomFormat
wird nicht exportiert. -
Dort deklarieren Sie
formats
als Slice mit drei Grußformaten. Wenn ein Slice deklariert wird, enthält die eckige Klammer keine Größenangabe, wie hier bei[]string
. Damit sagt man Go, dass sich die Größe dss Arrays, das hinter dem Slice steckt, dynamisch ändern kann. -
Sie benutzen das
math/rand
-Paket, um eine Zufallszahl zum Indexzugriff auf das Slice zu erhalten. -
Sie ergänzen eine
init
-Funktion um dort dasrand
-Paket mit der aktuellen Zeit zu "impfen" (seed).init
-Funktionen führt Go automatisch bei Programmstart aus, nachdem die globalen Variablen initialisiert worden sind. Mehr zuinit
-Funktionen finden Sie in "Effective Go" (de). -
Um einen Formatstring zu erhalten, rufen Sie in
Hello
dierandomFormat
-Funktion, und benutzen dieses Format zusammen mit dem Wert vonname
zum Erzeugen des Grußes. - Geben Sie — wie bisher — den Gruß (oder einen Fehler) zurück.
-
Sie fügen eine Funktion
-
Ändern Sie in
hello/hello.go
den Kode so, dass er wie unten aussieht.Sie ergänzen hier nur den Namen Rosa (oder einen anderen nach Belieben) als Argument für den Aufruf der
Hello
-Funktion inhello.go
:package main import ( "fmt" "log" "example.com/greetings" ) func main() { // Festlegen der Eigenschaften des Standard-Loggers: // ein Präfix für den Log-Eintrag und ein Schalter, der die // Ausgabe von Zeit, Quelldateiname und Zeilennummer unterbindet. log.SetPrefix("greetings: ") log.SetFlags(0) // Anfordern einer Grußnachricht message, err := greetings.Hello("Rosa") // Einen zurückgelieferter Fehler auf der Konsole ausgeben, // dann das Programm verlassen. if err != nil { log.Fatal(err) } // Wenn ohne Fehler, Ausgabe der Nachricht auf der Konsole. fmt.Println(message) }
-
Starten Sie
hello.go
auf der Kommandozeile imhello
-Ordner, um zu bestätigen, dass Ihr Kode funktioniert. Starten Sie mehrmals und beachten Sie die wechselnden Grußformeln.$ go run . Schön dich zu sehen, Rosa. $ go run . Hi Rosa. Wie geht's. $ go run . Rosa! Habe die Ehre!
Nun wollen wir mithilfe eines Slices mehrere Personen grüßen. Das geschieht im nächsten Schritt.
5. Rückgabe von Grußformeln für mehrere Adressaten
Als letzte Änderung an Ihrem Modulkode werden Sie die Fähigkeit hinzufügen, Grüße für mehrere Personen auf eine einzige Anfrage hin zurückzugeben. Anders formuliert nehmen wir eine Eingabe aus mehreren Werten entgegen, verknüpfen diese Eingabewerte mit Ausgabewerten und erhalten eine Ausgabe, die ebenfalls mehrere Werte enthält. Dazu muss eine Menge von Namen an eine Funktion übergeben werden, die für jeden davon eine Grußformel zurückgeben kann.
Die Sache hat nur einen Haken. Würden wir den Parameter
der Hello
-Funktion von einem einzelnen Namen
zu einer Namensmenge ändern, würde sich auch die Signatur
der Funktion ändern.
Wenn Sie also das Modul example.com/greetings
schon veröffentlicht hätten und andere bereits in ihrem Kode
Hello
aufgerufen hätten, würden deren Programme
unbrauchbar.
In so einem Fall ist es besser, eine neue Funktion mit neuem Namen zu schreiben. Diese neue Funktion wird dann andere Parameter entgegennehmen. So wird Rückwärtskompatibilität für die alte Funktion gewährleistet.
-
Ändern Sie greetings/greetings.go so, dass Ihr Kode wie dieser hier
ausschaut:
package greetings import ( "errors" "fmt" "math/rand" "time" ) // Hello gibt einen Gruß an die genannte Person zurück. func Hello(name string) (string, error) { // Wenn kein Name, dann Fehler mit Fehlermeldung zurückgeben. if name == "" { return "", errors.New("Name leer") } // Erzeugen eines Grußes in zufälliger ausgewählter Form. message := fmt.Sprintf(randomFormat(), name) return message, nil } // Hellos gibt eine Map zurück, in der alle genannten Personen // mit einer Grußbotschaft verknüpft sind. func Hellos(names []string) (map[string]string, error) { // Eine Map, um Namen mit Grüßen zu verknüpfen. messages := make(map[string]string) // Iterieren über das mitgegebene Slice mit den Namen und // und Rufen der Hello-Funktion für jeden Namen. for _, name := range names { message, err := Hello(name) if err != nil { return nil, err } // Verknüpfen von Name und Gruß in der Map. messages[name] = message } return messages, nil } // init versorgt Anfangswerte von Variablen, // die später in Funktionen benutzt werden. func init() { rand.Seed(time.Now().UnixNano()) } // randomFormat gibt aus einer Menge von Grußformeln // eine zurück; sie wird zufällig ausgewählt. func randomFormat() string { // Ein Slice von Formatstrings mit Grußformel. formats := []string{ "Hi %v. Wie geht's.", "Schön dich zu sehen, %v.", "%v! Habe die Ehre!", } // Rückgabe einer zufällig ausgewählten Grußformel. return formats[rand.Intn(len(formats))] }
In diesem Kode tun Sie folgendes:
-
Sie fügen eine
Hellos
-Funktion hinzu, dessen Parameter ein Slice mit Namen anstelle des einzelnen Namens ist. Sie ändern weiterhin den Typ des ersten Ergebnistyps von einem String zu einer Map, damit Sie die Namen verknüpft mit den jeweiligen Grußbotschaften zurückgeben können. -
Sie lassen die neue
Hellos
-Funktion die schon bestehendeHello
-Funktion aufrufen. Das vermeidet Kodeverdoppelung und es stehen trotzdem beide Funktionen zur Verfügung. -
Sie erzeugen für die Grüße eine Map, die jeden
der Namen (als Schlüssel) mit dem erzeugten Gruß (als Wert)
verknüpft. In Go initialisiert man eine solche Map mit
der folgenden Sntax:
make(map[Schlüsseltyp]Wertetyp)
. Lassen SieHellos
diese Map an den Rufer zurückgeben. Mehr zu Maps finden Sie in "Go maps in action" -
Sie iterieren über alle der Funktion mitgegebenen Namen,
stellen für jeden sicher, dass sein Wert nicht-leer ist,
und verknüpfen den Namen mit einer Grußbotschaft.
In der
for
-Schleife gibtrange
zwei Werte zurück: den Index und den Wert des aktuellen Elements. Den Index brauchen Sie nicht, also nutzen Sie den Leeren Bezeichner, (einen Unterstrich), um ihn zu ignorieren. Mehr dazu in "The blank identifier" (de)
-
Sie fügen eine
-
In Ihrem
hello/hello.go
übergeben Sie ein Slice mit Namen und geben dann den Inhalt der zurückerhaltenen Map aus.Ändern Sie in
hello.go
den Kode so, dass er wie hier aussieht:package main import ( "fmt" "log" "example.com/greetings" ) func main() { // Festlegen der Eigenschaften des Standard-Loggers: // ein Präfix für den Log-Eintrag und ein Schalter, der die // Ausgabe von Zeit, Quelldateiname und Zeilennummer unterbindet. log.SetPrefix("greetings: ") log.SetFlags(0) // Ein Slice mit Namen drin. names := []string{"Rosa", "Klara", "Karl"} // Anfordern der Grußnachrichten für die Namen. messages, err := greetings.Hellos(names) if err != nil { log.Fatal(err) } // Wenn ohne Fehler, Ausgabe der Map auf die Konsole. fmt.Println(messages) }
Mit diesem Änderungen tun Sie folgendes:
-
Sie legen eine Variable
names
als Slice-Typ mit drei Namen an. -
Sie übergeben die
names
-Variable als Argument an dieHellos
-Funktion.
-
Sie legen eine Variable
-
Um zu kontrollieren, dass der Kode auch funktioniert,
wechseln Sie auf der Kommandozeile in den Ordner, der
hello/hello.go
enthält und starten Siego run
.Die Ausgabe ist eine Textdarstellung der Map, worin Namen mit Grüßen verknüpft sind, etwa so:
$ go run . map[Karl:Karl! Habe die Ehre! Klara:Hi Klara. Wie geht's. Rosa:Schön dich zu sehen, Rosa.]
Diese Lektion stellte Ihnen Maps vor, um damit Schlüssel/ Wert-Paare abzubilden. Sie zeigte außerdem den Gedanken vom Erhalt der Rückwärtskompatibilität auf, indem für eine neue oder geänderte Funktionalität in einem Modul eine neue Funktion implementiert wurde. Mehr dazu in "Keeping your modules compatible"
Im nächsten Schritt werden Sie einen Komponententest mit Go-Standardmethoden erstellen.
6. Erstellen eines Tests
Nun, da Ihr Kode in einen stabilen Zustand ist (Gut gemacht!),
fügen Sie noch einen Test an. Testen während des Entwickelns kann
frühzeitig Fehler sichtbar machen, die sich mit den Änderungen
eingeschlichen haben. In dieser Lektion erstellen Sie einen Test
für die Hello
-Funktion.
Go's Standardverfahren für Komponententests macht es einfach, ganz
nebenbei zu testen. Mit ein paar Namenskonventionen, mit Go's
testing
-Paket und dem go test
-Kommando
können Sie Tests im Nu schreiben und ausführen.
-
Erstellen Sie im
greetings
-Ordner eine Datei mit Namengreetings_test.go
.Endet der Dateiname auf
_test.go
, so signalisiert das demgo
-Kommando, dass diese Datei Testfunktionen enthält. -
Fügen Sie folgenden Kode in
greetings_test.go
ein und sichern die Datei.package greetings import ( "testing" "regexp" ) // TestHelloName ruft greetings.Hello mit einem Namen // und prüft auf einen gültigen Rückgabewert. func TestHelloName(t *testing.T) { name := "Kurt" want := regexp.MustCompile(`\b`+name+`\b`) msg, err := Hello("Kurt") if !want.MatchString(msg) || err != nil { t.Fatalf(`Hello("Kurt") = %q, %v, erwartet: %#q, nil`, msg, err, want) } } // TestHelloEmpty ruft greetings.Hello mit einem leeren String // und prüft, ob auch ein Fehler zurückgegeben wurde. func TestHelloEmpty(t *testing.T) { msg, err := Hello("") if msg != "" || err == nil { t.Fatalf(`Hello("") = %q, %v, erwartet: "", error`, msg, err) } }
In diesem Kode tun Sie folgendes:
- Sie implementieren die Testfunktionen im selben Paket wie den zu testenden Kode.
-
Sie erstellen zwei Testfunktionen zum Testen der Funktion
greetings.Hello
. Testfunktionen haben Namen der FormTestName
, wobei Name etwas über den jeweiligen Test sagt. Außerdem erwarten Testfunktionen als Parameter einen Zeiger auftesting.T
aus demtesting
-Paket. Diesen Parameter brauchen Sie in Ihrem Test für Meldungen und zum Protokollschreiben. -
Sie implementieren zwei Tests:
-
TestHelloName
ruft dieHello
- Funktion mit einem Wertname
, zu dem die Funktion eine gültige Nachricht zurückliefern soll. Sollte die Funktion einen Fehler oder eine unerwartete Nachricht (eine, die den Namen nicht enthält) zurückgeben, benutzen Sie die MethodeFatalf
des Parameterst
, um eine Meldung auf der Konsole auszugeben und die Testausführung zu beenden. -
TestHelloEmpty
ruft dieHello
- Funktion mit einem leeren String. Dieser Test soll bestätigen, dass Ihre Fehlerbehandlung funktioniert. Sollte die Funktion einen nicht-leeren String oder keinen Fehler zurückgeben, benutzen Sie die MethodeFatalf
des Parameterst
, um eine Meldung auf der Konsole auszugeben und die Testausführung zu beenden.
-
-
Zum Ausführen der Tests starten Sie von der Kommandozeile aus dem
greetings
-Ordner heraus das Kommandogo test
.Das Kommando
go test
führt Testfunktionen (also solche, deren Namen mitTest
beginnen) aus Testdateien (also deren Namen mit_test.go
enden) aus. Sie können auch den Schalter-v
(verbose) benutzen, um eine wortreiche Ausgabe mit der Liste aller Tests und deren Ergebnisse zu erhalten.Die Tests sollten erfolgreich sein.
$ go test PASS ok example.com/greetings 0.364s $ go test -v === RUN TestHelloName --- PASS: TestHelloName (0.00s) === RUN TestHelloEmpty --- PASS: TestHelloEmpty (0.00s) PASS ok example.com/greetings 0.372s
-
Pfuschen Sie nun in der
greetings.Hello
-Funktion, um mal einen scheiternden Test zu Gesicht zu bekommen.Die Testfunktion
TestHelloName
prüft den Rückgabewert für den derHello
-Funktion mitgegebenen Namen. Um ein Scheitern beobachten zu können, ändern Sie die Funktiongreetings.Hello
so, dass ihr Ergebnis nicht länger den Namen enthält.Ändern Sie die
Hello
-Funktion ingreetings/greetings.go
wie unten zu sehen. Beachten Sie, dass die hervorgehobenen Zeilen den Rückgabewert der Funktion so ändern, als wäre das Argumentname
versehenlich entfernt worden.// Hello gibt einen Gruß an die genannte Person zurück. func Hello(name string) (string, error) { // Wenn kein Name, dann Fehler mit Fehlermeldung zurückgeben. if name == "" { return "", errors.New("Name leer") } // Erzeugen eines Grußes in zufälliger ausgewählter Form. // message := fmt.Sprintf(randomFormat(), name) message := fmt.Sprint(randomFormat()) return message, nil }
-
Zum Ausführen der Tests starten Sie von der Kommandozeile aus dem
greetings
-Ordner heraus das Kommandogo test
.Sie starten
go test
dieses Mal ohne den Schalter-v
. Die Ausgabe wird so nur Einzelheiten für die gescheiterten Tests enthalten, was übersichtlicher ist, wenn Sie viele Tests haben.TestHelloName
sollte scheitern,TestHelloEmpty
bleibt erfolgreich.$ go test --- FAIL: TestHelloName (0.00s) greetings_test.go:15: Hello("Kurt") = "Hi %v. Wie geht's.", <nil>, erwartet: `\bKurt\b`, nil FAIL exit status 1 FAIL example.com/greetings 0.182s
Im nächsten (und letzten) Schritt werden Sie erfahren, wie man kompiliert und installiert, um den Kode lokal ausführen zu können.
7. Fertigen und Installieren der Anwendung
In dieser letzten Lektion lernen Sie ein paar neue
go
-Kommandos kennen.
Das bisher benutzte Kommando go run
mag als Abkürzung zum Umwandeln und Starten eines Programms,
an dem Sie viel zu ändern haben, nützlich sein, es
erzeugt aber keine ausführbare Binärdatei
Diese Lektion führt zwei neue Kommandos zum Umwandeln von Kode ein:
-
Das
go build
-Kommando wandelt Pakete und ihre Abhängigkeiten um, installiert aber nichts. -
Das
go install
-Kommando wandelt um und installiert Pakete.
-
Starten Sie von der Kommandozeile im
hello
-Ordner dasgo build
-Kommando, um den Kode in eine ausführbare Binärdatei zu wandeln.$ go build
-
Zum Prüfen starten Sie von der Kommandozeile aus im
hello
-Ordner diehello
-Binärdatei.Das Ergebnis kann anders aussehen, wenn Sie den Kode in
greetings.go
nach dem Testen geändert haben.-
Unter Linux oder auf dem Mac:
$ ./hello map[Karl:Karl! Habe die Ehre! Klara:Hi Klara. Wie geht's. Rosa:Rosa! Habe die Ehre!.]
-
Unter Windows:
$ hello.exe map[Karl:Karl! Habe die Ehre! Klara:Hi Klara. Wie geht's. Rosa:Rosa! Habe die Ehre!.]
Sie haben jetzt ihre Anwendung zu einer Binärdatei kompiliert, so dass sie ausführbar ist. Doch um sie jederzeit starten zu können, müssen Sie sich entweder in dem Ordner befinden, der die Binärdatei enthält, oder Sie müssen das Kommando mitsamt Pfad angeben.
Deshalb installieren wir nun die ausführbare Datei, damit Sie sie ohne Angabe des Pfades starten können.
-
Unter Linux oder auf dem Mac:
-
Finden Sie den Go-Installationspfad, also wo das
go
-Kommando das aktuelle Paket installieren wird.Den Installationspfad kann man ermitteln, indem man ein
go list
-Kommando der folgenden Form ausführt:$ go list -f '{{.Target}}'
Die Ausgabe ergibt beispielsweise
/home/gopher/bin/hello
, was bedeutet, dass Binärdateien nach /home/gopher/bin installiert werden. Diese Angabe brauchen Sie für den nächsten Schritt. -
Ergänzen Sie den Suchpfad Ihres Systems um den oben ermittelten
Go-Installationspfad.
So braucht ein Aufruf dann keine Angabe über den Ort Ihrer Binärdatei mehr.
-
Unter Linux oder auf dem Mac führen Sie folgendes Kommando aus:
$ export PATH=$PATH:/path/to/your/install/directory
-
Unter Windows führen Sie folgendes Kommando aus:
set PATH=%PATH%;C:\path\to\your\install\directory
Alternativ: Wenn bereits ein Ordner für Binärdateien wie
$HOME/bin
in Ihrem Suchpfad existiert, und wenn Sie dort Ihre Go-Programme installieren wollen, dann können Sie für Go das Installationsziel durch Setzen der GOBIN-Variablen ändern. Dazu nutzen Sie das Kommandogo env
in der folgenden Form:$ go env -w GOBIN=/path/to/your/bin
oder
go env -w GOBIN=C:\path\to\your\bin
-
Unter Linux oder auf dem Mac führen Sie folgendes Kommando aus:
-
Nachdem Sie den Suchpfad aktualisiert haben, starten Sie das
Kommando
go install
zum Kompilieren und Installieren des Pakets.$ go install
-
Starten Sie jetzt Ihre Anwendung ganz einfach mit ihrem
Namen. Damit's interessanter wird, öffnen Sie ein neues
Kommandofenster und starten
hello
von einem anderen Ordner aus.$ hello map[Karl:Karl! Habe die Ehre! Klara:Hi Klara. Wie geht's. Rosa:Rosa! Habe die Ehre!.]
Damit ist diese Übung zu Ende.
Zusammenfassung
Während dieser Übung haben Sie Funktionen erstellt, die Sie in zwei Module verpackt haben: eine mit Logik zum Erzeugen von Grußformeln; die andere als Konsument für die erstere.
Weitergehende Informationen zum Verwalten von Abhängigkeiten finden Sie unter "Managing dependencies", mehr zur Entwicklung von Modulen unter "Developing and publishing modules".
Und viele andere Eigenschaften der Programmiersprache Go werden präsentiert in der "Tour of Go".