
Pakete
Zusammengehörige Funktionen und Datentypen, die eine logische Einheit bilden, fasst man in Pakete zusammen. Die Standard-Bibliothek, die mit Go mitgeliefert wurde, enthält eine ganze Reihe nützlicher Pakete, zum Beispiel zum Lesen von komprimierten Dateien, zum Berechnen von Prüfsummen oder zum Lesen und Schreiben von XML-Dateien. Insgesamt sind es gut 150 Pakete, die man unter https://golang.org/pkg/ ansehen und aus jedem Go-Programm benutzen kann.
Importieren von Paketen
Um die Funktionen aus einem Paket zu benutzen, muss man es importieren.
In den bisherigen Beispielen wurde bisher nur das Paket fmt
benutzt.
Um mehrere import
Anweisungen zusammenzufassen, kann man folgende Syntax benutzen:
import (
"net/http"
"io/ioutil"
"fmt"
)
Ansprechbar sind die dann Pakete unter dem letzten Namensbestandteil, hier also http
,
ioutil
und fmt
, beispielsweise:
fmt.Println("Hallo")
Man kann (das wird aber sehr selten gemacht) Pakete unter einem anderen Namen importieren, als es gespeichert ist. Man kann auch Pakete laden, ohne sie zu benutzen. Das wird für Seiteneffekte benutzt (z.B. laden eines speziellen Datenbanktreibers). Auch kann man die Definitionen der Funktionen in den eigenen Namensraum importieren:
import (
formatiere "fmt"
. "io/ioutil"
"net/http"
_ "github.com/go-sql-driver/mysql"
)
func main() {
formatiere.Println("Hallo")
// normalerweise: ioutil.ReadFile(...)
data, err := ReadFile("liesmich.txt")
...
}
Hier wird das Paket fmt
unter dem Namen formatiere
bekannt gemacht und die
Definitionen im Paket ioutil
in den aktuellen Namensraum importiert (jetzt
kann man die Funktion ReadFile()
ohne ioutil
davor benutzen).
Ebenfalls wird das Paket mysql
geladen, aber ohne dass man es ansprechen kann.
Der Effekt ist, dass die entsprechende init()
-Funktion aufgerufen wird, in der der SQL-Treiber registriert wird
Ich erlaube mir aber die Bemerkung, dass man solche »Tricks« wohl überlegt einsetzen sollte, sie können mehr Verwirrung stiften als notwendig.
Sichtbarkeitsregeln
Vielleicht hast du schon bemerkt, dass die Funktionsnamen aus den Paketen, die wir benutzen, alle mit einem Großbuchstaben anfangen (Println()
, ReadFile()
, …).
Denn nur Funktionsnamen mit einem Großbuchstaben an erster Stelle sind von außen sichtbar.
Das ist eine äußerst praktische Eigenschaft von Go, die am Anfang vielleicht etwas Kopfschütteln hervorrufen mag, sich in der Praxis aber hervorragend bewährt.
Die Funktion addiere()
kann nur innerhalb eines Paketes aufgerufen werden, die Funktion Addiere()
(mit großem A) auch von außerhalb.
Damit kann man interne Funktionen vor dem Aufruf von außen verstecken.
Meist stellt man den Benutzern des Pakets nur wenige Funktionen zur Verfügung, die sie benutzen dürfen und weitere Funktionen, die intern aufgerufen werden.
Genau so verhält es sich mit Variablen und Konstanten (darauf kommen ich später zurück) und mit Strukturen (struct).
Um es etwas genauer, aber komplizierter zu formulieren: die identifier (Namen), die auf höchster Ebene eines Paket sichtbar sind, können von anderen Paketen benutzt werden. Diese identifier nennt man auch exported (exportiert).
In diesem Beispiel sind die Struktur Fahrzeug
, als auch das Feld Name
von
anderen Paketen ansprechbar.
type Fahrzeug struct {
Name string
hubraum int
ps int
kmh int
baujahr int
}
Ein anderes Paket könnte die Struktur Fahrzeug
initialisieren und den Namen
setzen, andere Felder nicht. Falls die Struktur Fahrzeug
im Paket katalog
definiert ist, können wir wie folgt darauf zugreifen:
package main
import (
"fmt"
"katalog"
)
func main() {
pkw := katalog.Fahrzeug{}
pkw.Name = "Audi 80"
// was aber nicht geht, weil hubraum
// mit einem Kleinbuchstaben anfängt:
// fmt.Println(pkw.hubraum)
}
Eigene Pakete erstellen
Es ist sehr leicht, ein eigenes Paket in Go zu erstellen. Es ist sogar sehr üblich, Funktionalität, die einen gewissen Umfang überschreitet, in ein eigenes Paket zu schreiben. Pakete haben eine ganze Reihe von Vorteilen: Kapselung, Wartbarkeit, Wiederverwendbarkeit und Testbarkeit. Unter Kapselung versteht man wie oben erwähnt das Verstecken von internen Funktionen vor dem Benutzer des Pakets. Wenn man die internen Funktionen (und Strukturen, Variablen etc.) nicht offenbart, kann man sie leicht ändern, ohne dass existierende Programme nicht mehr funktionieren. Die Wartbarkeit wird erhöht, weil man immer einen in sich begrenzten Funktionsumfang überblicken muss. Wiederverwendbarkeit ist eine der großen Errungenschaften von Paketen. Im Idealfall kann man Pakete in anderen Programmen erneut verwenden und somit erheblich Arbeitsaufwand und Fehlerquellen einsparen. Auf die Testbarkeit wird in einem späteren Abschnitt noch einmal genauer eingegangen.
Ein Paket zu erstellen besteht aus wenigen Schritten:
-
Es muss ein Verzeichnis mit dem Namen des Pakets erstellt werden (z.B.
katalog
für das Paketkatalog
). - In diesem Verzeichnis liegen beliebig viele Quelldateien, die zu dem Paket gehören.
- Jede Datei muss als Paketdeklaration den Paketnamen enthalten.
Neben der nachfolgenden Erklärung über GOPATH gibt es seit ein paar Jahren die Möglichkeit, Module zu definieren und zu benutzen. Welche Methode man benutzt, kommt auf die lokalen Gegebenheiten an. GOPATH ist m.E. etwas leichter einzurichten, Module sind flexibler. Die Module beschreibe ich an einer anderen Stelle. |
Um die Sache ein kleinwenig schwieriger zu machen, kommt jetzt die
Umgebungsvariable GOPATH
ins Spiel. Das Verzeichnis des Pakets muss in dem Verzeichnis src
liegen, das in dieser Umgebungsvariable
enthalten ist, wenn es außerhalb der Go-Installation liegt[1]. Meist setzt man die Variable auf ein festes Verzeichnis (z.B.
/home/meinname/go
) oder man setzt die Variable für jedes Projekt separat,
was in der Praxis wohl häufiger anzutreffen ist. Setzt man also GOPATH
auf das aktuelle Verzeichnis, erstellt man in hier ein Unterverzeichnis mit dem Namen src
und darunter das Verzeichnis mit dem Paketname. Wollen wir als Beispiel für ein Autohaus einen Katalog der Fahrzeuge programmieren, könnten wir das Paket katalog
wie folgt erzeugen (eine Unix-Shell vorausgesetzt):
export GOPATH=$PWD
mkdir -p src/katalog
In diesem Verzeichnis legt man dann eine Datei mit der Endung .go
an,
beispielsweise katalog.go
die mindestens die Zeile package katalog
am
Anfang der Datei enthält.
GOPATH
zeigt auf das Verzeichnis, in dem das Verzeichnis src
enthalten ist. Das Paket katalog
besteht hier aus drei Dateien.Externe Pakete laden
Es gibt eine Reihe nützlicher Pakete auf bekannten Source-Repositories wie
Github, Bitbucket, Google code, Launchpad oder anderen. Go macht es dem
Anwender sehr leicht, Pakete und deren Abhängigkeiten von diesen Servern
herunter zu laden und in das eigene Programm einzubinden. Der Befehl hierzu
heißt go get
und hat als Parameter den Pfad zum Repository. go get
unterstützt die Versionsverwaltungen Git, Mercurial, Subversion und Bazaar.
Möchte man beispielsweise im eigenen Programm das Paket gogit
des Autors
dieser Einführung benutzen (mit dem Paket kann man von Go heraus auf Git-
Repositories zugreifen), lautet der Befehl auf der Kommandozeile:
go get github.com/speedata/gogit
Go erzeugt unterhalb des Pfades, der in der Variable $GOPATH
angegeben ist,
die Verzeichnishierarchie src/github.com/speedata/gogit
. Der Import-Pfad,
um das Paket dann zu benutzen, ist github.com/speedata/gogit
. Angesprochen,
das wurde oben bereits erwähnt, wird das Paket mit dem letzten
Namensbestandteil, hier gogit
:
package main
import "github.com/speedata/gogit"
func main() {
repos, err := gogit.OpenRepository("/pfad/zum/repository")
if err != nil {
// Fehlerbehandlung
}
// ...
}
GOPATH
enthalten sein