Exportierte vs. nicht exportierte Felder
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 47 von 107.
In Go wird die Kapselung auf Strukturebene durch die Groß- und Kleinschreibung der Feldnamen gesteuert. Diese einfache Regel bestimmt, ob auf Felder von außerhalb des Pakets zugegriffen werden kann, in dem die Struktur definiert ist.
Felder, die mit einem Großbuchstaben beginnen, sind exportiert: aus jedem Paket zugänglich. Felder, die mit einem Kleinbuchstaben beginnen, sind nicht exportiert: nur innerhalb desselben Pakets zugänglich:
// Im Paket "user"
type User struct {
Name string // Exportiert - überall zugänglich
Email string // Exportiert - überall zugänglich
password string // Unexportiert - nur im Paket "user" zugänglich
age int // Unexportiert - nur im Paket "user" zugänglich
}
Wenn ein anderes Paket diese Struktur importiert und verwendet, kann es nur mit exportierten Feldern interagieren:
// Im Paket "main"
import "user"
func main() {
u := user.User{
Name: "Alice", // funktioniert
Email: "a@mail.com", // funktioniert
// password: "secret", // Kompilierfehler: unbekanntes Feld
}
fmt.Println(u.Name) // funktioniert
// fmt.Println(u.password) // Kompilierfehler: kann nicht auf unexportiertes Feld verweisen
}
Dieser Mechanismus schützt sensible Daten und interne Implementierungsdetails. Das Feld password kann von außerhalb des Pakets nicht direkt gelesen oder geändert werden, sodass anderer Code gezwungen ist, die von dir bereitgestellten Methoden zu verwenden. Dies ist Gos Ansatz zur Erreichung von Datenverbergung: ein grundlegendes Prinzip der Kapselung in der objektorientierten Programmierung.
Aufgabe
EinfachErstellen wir ein Bankkontosystem, das die Kapselung durch exportierte und nicht exportierte Felder demonstriert. Du erstellst ein Paket, das sensible Finanzdaten schützt und gleichzeitig sichere öffentliche Informationen bereitstellt.
Du organisierst deinen Code über zwei Dateien:
account.go: Erstelle eineBankAccount-Struktur immain-Paket, die ein echtes Bankkonto mit angemessenem Datenschutz modelliert:HolderName(exportiert) – der Name des Kontoinhabers, der öffentlich angezeigt werden kannAccountType(exportiert) – die Art des Kontos (z. B. „Savings“, „Checking“)balance(nicht exportiert) – der tatsächliche Kontostand, der geschützt werden solltepin(nicht exportiert) – die PIN des Kontos, hochsensible Daten
NewBankAccounthinzu, die den Namen des Kontoinhabers, die Kontoart, den Anfangssaldo und die PIN entgegennimmt und anschließend einen Zeiger auf eine neueBankAccountzurückgibt. Füge außerdem eine MethodeGetPublicInfo() stringhinzu, die das folgende Format zurückgibt:Account: [HolderName] ([AccountType])main.go: Lies die Kontodaten aus der Eingabe ein, erstelle mithilfe des Konstruktors einBankAccountund demonstriere die Kapselung. Gib die öffentlichen Informationen mithilfe der Methode aus und greife anschließend direkt auf jedes exportierte Feld zu, wobei du sie in separaten Zeilen ausgibst. Die nicht exportierten Felder (balanceundpin) sind geschützt. Du kannst sie nur über den Konstruktor festlegen.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Name des Kontoinhabers
- Zeile 2: Kontoart
- Zeile 3: Anfangssaldo (als Gleitkommazahl)
- Zeile 4: PIN-Code
Wenn beispielsweise Alice Johnson, Savings, 5000.50 und 1234 angegeben werden, sollte deine Ausgabe folgendermaßen aussehen:
Account: Alice Johnson (Savings)
Holder: Alice Johnson
Type: SavingsBeachte, dass der Kontostand und die PIN niemals in der Ausgabe erscheinen: Sie sind sicher innerhalb der Struktur verborgen und nur innerhalb des Pakets zugänglich. Das ist Kapselung in der Praxis: Sensible Daten werden geschützt, während öffentliche Informationen weiterhin zugänglich bleiben.
Probier es selbst
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Kontoinhaber-Namen lesen
holderName, _ := reader.ReadString('\n')
holderName = strings.TrimSpace(holderName)
// Kontotyp lesen
accountType, _ := reader.ReadString('\n')
accountType = strings.TrimSpace(accountType)
// Anfangssaldo lesen
balanceStr, _ := reader.ReadString('\n')
balanceStr = strings.TrimSpace(balanceStr)
balance, _ := strconv.ParseFloat(balanceStr, 64)
// PIN lesen
pinStr, _ := reader.ReadString('\n')
pin := strings.TrimSpace(pinStr)
// TODO: Erstelle ein neues BankAccount mit dem Konstruktor
// account := NewBankAccount(...)
// TODO: Gib die öffentlichen Infos mit der GetPublicInfo()-Methode aus
// TODO: Gib den Inhabernamen aus, indem du direkt auf das exportierte Feld zugreifst
// fmt.Println("Holder:", ...)
// TODO: Gib den Kontotyp aus, indem du direkt auf das exportierte Feld zugreifst
// fmt.Println("Type:", ...)
// Note: balance und pin sind unexportiert – auf sie kann hier nicht direkt zugegriffen werden
// Sie sind sicher innerhalb der struct gekapselt
_ = balance // Entferne diese Zeile, wenn du balance verwendest
_ = pin // Entferne diese Zeile, wenn du pin verwendest
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP in Go
Externe DateienGo-Workspace & ModulePackages & ImportsExportierte vs. nicht exportierte NamenEinführung in OOP mit GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktorfunktionenRückblick – Einfacher Taschenrechner4Schnittstellen
Einführung in SchnittstellenImplizite ImplementierungSchnittstelle als VertragLeere Schnittstelle (any)TypzusicherungTypwechselZusammensetzung von SchnittstellenStringer- und Error-SchnittstellenRückblick – Formenrechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf PaketebeneGetter- und Setter-MethodenInformationsverbergung in GoRückblick – Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterTypbeschränkungenGenerische StrukturenWorkaround für generische MethodenZusammenfassung – Generische Sammlung2Typen & Structs im Detail
Grundlegende & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct-TagsAnonyme StructsVerschachtelte StructsNullwerte & StandardwerteRückblick – Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen der Struct-EinbettungMethoden-PromotionMehrere Structs einbettenEinbettung vs. AggregationVerbergen eingebetteter MethodenRückblick – Mitarbeiterhierarchie8Fehlerbehandlung & OOP
Das Error-InterfaceBenutzerdefinierte FehlertypenError-Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverRückblick – Dateiparser3Zeiger & Speicher
Grundlagen von Zeigern in GoZeiger auf StructsÜbergabe per Wert vs. ReferenzDie Funktion new()Garbage Collection in GoRückblick – Verkettete Liste erstellen6Polymorphismus in Go
Polymorphismus über InterfacesDuck-Typing in GoRegeln zur Interface-ErfüllungPolymorphe SammlungenDependency InjectionZusammenfassung – Zahlungsprozessor9Konkurrenz & OOP
Grundlagen der GoroutinesChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignRückblick – Worker PoolÜbe selbstständig: Online-Go-Compiler