Menu
Coddy logo textTech

Kapselung auf Paket-Ebene

Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy — Lektion 48 von 107.

Während die vorherige Lektion die Sichtbarkeit auf Feldebene behandelte, findet die Kapselung in Go tatsächlich auf der Paketebene statt. Alles innerhalb desselben Pakets – unabhängig davon, in welcher Datei es sich befindet – kann auf nicht exportierte Felder und Funktionen zugreifen.

Betrachten Sie ein Paket mit mehreren Dateien:

// account/account.go
package account

type Account struct {
    balance int  // nicht exportiert
}

func (a *Account) Deposit(amount int) {
    a.balance += amount
}
// account/helper.go
package account

func ResetAccount(a *Account) {
    a.balance = 0  // funktioniert - gleiches Paket
}

Beide Dateien gehören zum account-Package, sodass helper.go direkt auf das nicht exportierte balance-Feld zugreifen kann. Dies ist beabsichtigt – es ermöglicht Ihnen, die Package-Logik auf mehrere Dateien aufzuteilen und gleichzeitig den internen Zugriff beizubehalten.

Code außerhalb des Pakets kann jedoch nicht auf nicht-exportierte Member zugreifen:

// main.go
package main

import "account"

func main() {
    acc := &account.Account{}
    acc.Deposit(100)        // funktioniert - exportierte Methode
    // acc.balance = 50     // Fehler - nicht-exportiertes Feld
    // account.ResetAccount // funktioniert, wenn ResetAccount exportiert wäre
}

Diese Grenze auf Paketebene ist der primäre Kapselungsmechanismus von Go. Sie entwerfen Pakete als zusammenhängende Einheiten, in denen interner Code frei zusammenarbeitet, während externer Code nur über die exportierte API interagiert. Betrachten Sie jedes Paket als ein eigenständiges Modul mit einer klaren öffentlichen Schnittstelle.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Bestandsverwaltungssystem erstellen, das zeigt, wie mehrere Dateien innerhalb desselben Pakets zusammenarbeiten können, indem sie direkt auf nicht exportierte Felder zugreifen, während diese Daten vor externem Code verborgen bleiben.

Sie werden drei Dateien erstellen, die im main-Paket zusammenarbeiten:

  • product.go: Definieren Sie ein Product-Struct mit einem exportierten Name-Feld und einem nicht exportierten quantity-Feld (int). Fügen Sie eine exportierte Methode Info() string hinzu, die das Format [Name]: [quantity] in stock zurückgibt.
  • inventory.go: Erstellen Sie Hilfsfunktionen, die mit Produkten arbeiten, indem sie direkt auf das nicht exportierte quantity-Feld zugreifen (möglich, da sie sich im selben Paket befinden). Implementieren Sie:
    • Restock(p *Product, amount int) - fügt den Betrag zur Menge des Produkts hinzu
    • Sell(p *Product, amount int) bool - subtrahiert den Betrag von der Menge, wenn genügend Bestand vorhanden ist; gibt true zurück, wenn erfolgreich, oder false, wenn nicht genügend Bestand vorhanden ist
    • NewProduct(name string, initialQty int) *Product - Konstruktor, der ein Produkt mit dem angegebenen Namen und der Anfangsmenge erstellt
  • main.go: Lesen Sie Produktdetails aus der Eingabe, erstellen Sie ein Produkt mit dem Konstruktor, führen Sie Bestandsoperationen durch und zeigen Sie die Ergebnisse an. Geben Sie nach jeder Operation die Produktinfo aus, um den aktualisierten Zustand anzuzeigen.

Die folgenden Eingaben werden bereitgestellt:

  • Zeile 1: Produktname
  • Zeile 2: Anfangsmenge (Ganzzahl)
  • Zeile 3: Nachfüllmenge (Ganzzahl)
  • Zeile 4: Verkaufsmenge (Ganzzahl)

Geben Sie nach dem Erstellen des Produkts dessen Anfangsinfo aus. Füllen Sie dann den Bestand auf und geben Sie die Info erneut aus. Versuchen Sie schließlich zu verkaufen und geben Sie entweder die aktualisierte Info (bei Erfolg) oder "Sale failed: insufficient stock" (bei unzureichender Menge) aus.

Zum Beispiel, bei "Laptop", 10, 5 und 8 sollte Ihre Ausgabe wie folgt aussehen:

Laptop: 10 in stock
Laptop: 15 in stock
Laptop: 7 in stock

Und bei "Phone", 3, 2 und 10 sollte Ihre Ausgabe wie folgt aussehen:

Phone: 3 in stock
Phone: 5 in stock
Sale failed: insufficient stock

Die entscheidende Erkenntnis hierbei ist, dass inventory.go das nicht exportierte quantity-Feld direkt lesen und ändern kann, da es sich im selben Paket wie product.go befindet. Dies ist Kapselung auf Paketebene in Aktion – interne Zusammenarbeit geschieht frei, während die Daten vor externen Paketen geschützt bleiben.

Spickzettel

Gos Kapselung erfolgt auf Paketebene, nicht auf Dateiebene. Alle Dateien innerhalb desselben Pakets können auf nicht exportierte Felder und Funktionen zugreifen, unabhängig davon, in welcher Datei sie definiert sind.

Zugriff auf Paketebene

Nicht exportierte Mitglieder sind über alle Dateien im selben Paket hinweg zugänglich:

// account/account.go
package account

type Account struct {
    balance int  // nicht exportiert
}

func (a *Account) Deposit(amount int) {
    a.balance += amount
}
// account/helper.go
package account

func ResetAccount(a *Account) {
    a.balance = 0  // funktioniert - gleiches Paket
}

Einschränkungen für externe Pakete

Code außerhalb des Pakets kann nicht auf nicht exportierte Mitglieder zugreifen:

// main.go
package main

import "account"

func main() {
    acc := &account.Account{}
    acc.Deposit(100)        // funktioniert - exportierte Methode
    // acc.balance = 50     // Fehler - nicht exportiertes Feld
}

Dieses Design ermöglicht es Ihnen, die Paketlogik auf mehrere Dateien aufzuteilen und gleichzeitig den internen Zugriff beizubehalten, wodurch kohärente Einheiten mit klaren öffentlichen Schnittstellen entstehen.

Probier es selbst

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Produktnamen lesen
	name, _ := reader.ReadString('\n')
	name = strings.TrimSpace(name)

	// Anfangsmenge lesen
	initialQtyStr, _ := reader.ReadString('\n')
	initialQty, _ := strconv.Atoi(strings.TrimSpace(initialQtyStr))

	// Nachfüllmenge lesen
	restockStr, _ := reader.ReadString('\n')
	restockAmount, _ := strconv.Atoi(strings.TrimSpace(restockStr))

	// Verkaufsmenge lesen
	sellStr, _ := reader.ReadString('\n')
	sellAmount, _ := strconv.Atoi(strings.TrimSpace(sellStr))

	// TODO: Ein neues Produkt mit dem NewProduct-Konstruktor erstellen

	// TODO: Initiale Produktinformationen ausgeben

	// TODO: Das Produkt auffüllen und Informationen ausgeben

	// TODO: Verkauf versuchen und Ergebnis ausgeben
	// Wenn der Verkauf erfolgreich ist, die aktualisierten Informationen ausgeben
	// Wenn der Verkauf fehlschlägt, "Sale failed: insufficient stock" ausgeben

	fmt.Println() // Durch tatsächliche Ausgabe ersetzen
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung