Information Hiding in Go
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy — Lektion 50 von 107.
Information Hiding geht über das bloße Nicht-Exportieren von Feldern hinaus. Es geht darum, Ihre Typen so zu entwerfen, dass externer Code nur das erfährt, was er wissen muss – nicht mehr.
Das Ziel ist es, Verhalten durch Methoden offenzulegen, während die interne Struktur vollständig verborgen bleibt. Betrachten Sie diesen Ansatz:
type Counter struct {
value int
maxLimit int
}
func NewCounter(limit int) *Counter {
return &Counter{maxLimit: limit}
}
func (c *Counter) Increment() bool {
if c.value >= c.maxLimit {
return false
}
c.value++
return true
}
func (c *Counter) Value() int {
return c.value
}
Externer Code weiß nicht, dass Counter intern ein int verwendet. Es könnte ein float64, ein Slice oder etwas ganz anderes sein. Die Implementierung kann sich ändern, ohne Code zu beeinflussen, der Counter verwendet – solange sich die Methoden auf die gleiche Weise verhalten.
Dieses Prinzip gilt auch für Hilfsfunktionen. Halten Sie die interne Logik unexportiert:
// Exportiert - Teil der öffentlichen API
func (c *Counter) Reset() {
c.value = 0
}
// Nicht exportiert - interner Helfer
func (c *Counter) isAtLimit() bool {
return c.value >= c.maxLimit
}
Indem Sie isAtLimit() verbergen, steht es Ihnen frei, dies später zu ändern oder zu entfernen. Das Verbergen von Informationen schafft eine klare Grenze zwischen dem, was Ihr Paket externen Benutzern verspricht, und wie es intern funktioniert.
Aufgabe
EinfachLassen Sie uns ein sicheres Wallet-System bauen, das Information Hiding (Datenkapselung) demonstriert. Sie werden ein digitales Wallet erstellen, bei dem die interne Kontostandsverfolgung und die Transaktionshistorie vollständig vor externem Code verborgen sind – nur das Verhalten wird über Methoden offengelegt.
Sie werden Ihren Code in zwei Dateien organisieren:
wallet.go: Erstellen Sie einWallet-Struct, das seinen gesamten internen Zustand verbirgt. Das Wallet sollte den aktuellen Kontostand und ein Ausgabenlimit intern verfolgen, aber externer Code sollte niemals wissen, wie diese gespeichert oder dargestellt werden. Legen Sie das Verhalten über diese Methoden offen:NewWallet(limit float64) *Wallet- Konstruktor, der ein Wallet mit dem angegebenen Ausgabenlimit und einem Kontostand von Null erstelltDeposit(amount float64) bool- fügt dem Wallet Geld hinzu, gibttruezurück, wenn erfolgreich (Betrag muss positiv sein)Spend(amount float64) bool- hebt Geld ab, wenn ausreichend Guthaben vorhanden ist UND der Betrag das Ausgabenlimit nicht überschreitet, gibttruezurück, wenn erfolgreichBalance() float64- gibt den aktuellen Kontostand zurückStatus() string- gibt eine Statusmeldung im Format:Balance: $[balance] (Limit: $[limit])zurück
main.go: Lesen Sie die Wallet-Konfiguration und Transaktionen aus der Eingabe ein, erstellen Sie ein Wallet, führen Sie Operationen aus und zeigen Sie die Ergebnisse nach jeder Aktion an.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Ausgabenlimit
- Zeile 2: Einzahlungsbetrag
- Zeile 3: Erster Ausgabenbetrag
- Zeile 4: Zweiter Ausgabenbetrag
Geben Sie nach dem Erstellen des Wallets seinen Anfangsstatus aus. Führen Sie dann jede Operation aus und geben Sie entweder den aktualisierten Status (falls erfolgreich) oder Transaction failed (falls die Operation abgelehnt wurde) aus. Formatieren Sie alle Dollarbeträge mit zwei Dezimalstellen.
Zum Beispiel, gegeben 50, 100, 30 und 80, sollte Ihre Ausgabe wie folgt aussehen:
Balance: $0.00 (Limit: $50.00)
Balance: $100.00 (Limit: $50.00)
Balance: $70.00 (Limit: $50.00)
Transaction failedDie letzte Transaktion schlägt fehl, da $80 das Ausgabenlimit von $50 überschreitet – obwohl genügend Guthaben vorhanden ist. Das Wallet setzt seine Regeln intern durch, ohne offenzulegen, wie es diese Entscheidungen trifft. Externer Code ruft einfach Methoden auf und erhält Ergebnisse, ohne jemals die internen Implementierungsdetails zu kennen.
Probier es selbst
package main
import (
"fmt"
)
func main() {
// Eingaben lesen
var limit float64
var depositAmount float64
var spend1 float64
var spend2 float64
fmt.Scanln(&limit)
fmt.Scanln(&depositAmount)
fmt.Scanln(&spend1)
fmt.Scanln(&spend2)
// TODO: Erstelle eine neue Wallet mit dem angegebenen Ausgabenlimit
// TODO: Initialen Status ausgeben
// TODO: Einzahlung durchführen und Ergebnis ausgeben (Status oder "Transaction failed")
// TODO: Erste Ausgabe tätigen und Ergebnis ausgeben
// TODO: Zweite Ausgabe tätigen und Ergebnis ausgeben
}
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 Go OOP
Externe DateienGo Workspace & ModulePackages & ImportsExportierte vs. nicht-exportierte NamenEinführung in OOP in GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktor-FunktionenZusammenfassung – Einfacher Taschenrechner4Interfaces
Einführung in InterfacesImplizite ImplementierungInterface als VertragLeeres Interface (any)Type AssertionType SwitchInterface-KompositionStringer & Error InterfacesRückblick - Formen-Rechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf Paket-EbeneGetter- & Setter-MethodenInformation Hiding in GoZusammenfassung - Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterType ConstraintsGenerische StructsWorkaround für generische MethodenZusammenfassung – Generische Collection2Vertiefung: Typen & Structs
Basis- & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct TagsAnonyme StructsVerschachtelte StructsZero Values & StandardwerteZusammenfassung - Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen des Struct EmbeddingMethod PromotionEinbetten mehrerer StructsEmbedding vs. AggregationShadowing eingebetteter MethodenZusammenfassung – Mitarbeiter-Hierarchie8Fehlerbehandlung & OOP
Das error-InterfaceEigene FehlertypenError Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverZusammenfassung – Datei-Parser3Zeiger & Speicher
Zeiger-Grundlagen in GoZeiger auf StructsWertübergabe vs. ReferenzübergabeDie new() FunktionGarbage Collection in GoWiederholung – Linked List Builder6Polymorphismus in Go
Polymorphismus über InterfacesDuck Typing in GoInterface-ErfüllungsregelnPolymorphe CollectionsDependency InjectionZusammenfassung – Payment Processor9Nebenläufigkeit & OOP
Goroutines GrundlagenChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignZusammenfassung - Worker Pool