Menu
Coddy logo textTech

Kapselung auf Paketebene

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

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

Stell dir ein Paket mit mehreren Dateien vor:

// 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-Paket, daher kann helper.go direkt auf das nicht exportierte balance-field zugreifen. Das ist beabsichtigt. Dadurch kannst du die Paketlogik auf mehrere Dateien aufteilen und gleichzeitig den internen Zugriff beibehalten.

Code außerhalb des Pakets kann jedoch 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
    // account.ResetAccount // funktioniert, wenn ResetAccount exportiert wäre
}

Diese Grenze auf Paketebene ist Gos wichtigster Mechanismus zur Kapselung. Du entwirfst Pakete als zusammenhängende Einheiten, in denen der interne Code frei zusammenarbeitet, während externer Code nur über die exportierte API interagiert. Betrachte jedes Paket als ein eigenständiges Modul mit einer klaren öffentlichen Schnittstelle.

challenge icon

Aufgabe

Einfach

Erstellen wir ein Inventarverwaltungssystem, 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.

Du erstellst drei Dateien, die im Paket main zusammenarbeiten:

  • product.go: Definiere eine Product-Struktur mit einem exportierten Feld Name und einem nicht exportierten Feld quantity (int). Füge eine exportierte Methode Info() string hinzu, die das folgende Format zurückgibt: [Name]: [quantity] in stock
  • inventory.go: Erstelle Hilfsfunktionen, die mit Produkten arbeiten, indem sie direkt auf das nicht exportierte Feld quantity zugreifen (was möglich ist, weil sie sich im selben Paket befinden). Implementiere:
    • Restock(p *Product, amount int) – addiert den Betrag zur Menge des Produkts
    • Sell(p *Product, amount int) bool – zieht den Betrag von der Menge ab, wenn ausreichend Bestand vorhanden ist, und gibt true zurück, wenn der Vorgang erfolgreich war, oder false, wenn der Bestand nicht ausreicht
    • NewProduct(name string, initialQty int) *Product – Konstruktor, der ein Produkt mit dem angegebenen Namen und der anfänglichen Menge erstellt
  • main.go: Lies die Produktdetails aus der Eingabe, erstelle mithilfe des Konstruktors ein Produkt, führe Inventaroperationen aus und zeige die Ergebnisse an. Gib nach jeder Operation die Produktinformationen aus, um den aktualisierten Zustand anzuzeigen.

Die folgenden Eingaben werden bereitgestellt:

  • Zeile 1: Produktname
  • Zeile 2: Anfängliche Menge (Ganzzahl)
  • Zeile 3: Nachschubmenge (Ganzzahl)
  • Zeile 4: Verkaufsmenge (Ganzzahl)

Gib nach dem Erstellen des Produkts zunächst dessen Anfangsinformationen aus. Führe dann den Nachschub durch und gib die Informationen erneut aus. Versuche schließlich, das Produkt zu verkaufen, und gib entweder die aktualisierten Informationen (bei Erfolg) oder Sale failed: insufficient stock aus (wenn die Menge nicht ausreicht).

Wenn beispielsweise Laptop, 10, 5 und 8 angegeben werden, sollte deine Ausgabe folgendermaßen aussehen:

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

Und wenn Phone, 3, 2 und 10 angegeben werden, sollte deine Ausgabe folgendermaßen aussehen:

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

Die wichtigste Erkenntnis ist, dass inventory.go das nicht exportierte Feld quantity direkt lesen und ändern kann, weil es sich im selben Paket wie product.go befindet. Dies ist Kapselung auf Paketebene in der Praxis: Die interne Zusammenarbeit findet uneingeschränkt statt, während die Daten vor externen Paketen geschützt bleiben.

Probier es selbst

package main

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

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

	// Produktname 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: Erstelle ein neues Produkt mit dem NewProduct-Konstruktor

	// TODO: Gib die initialen Produktinformationen aus

	// TODO: Fülle das Produkt nach und gib die Informationen aus

	// TODO: Versuche zu verkaufen und gib das Ergebnis aus
	// Wenn der Verkauf erfolgreich ist, gib die aktualisierten Informationen aus
	// If sale fails, print "Sale failed: insufficient stock"

	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

Übe selbstständig: Online-Go-Compiler