Menu
Coddy logo textTech

Encapsulation au niveau du package

Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy — leçon 48 sur 107.

Alors que la leçon précédente portait sur la visibilité au niveau des champs, l'encapsulation de Go fonctionne en réalité au niveau du package. Tout ce qui se trouve au sein du même package — quel que soit le fichier dans lequel il se trouve — peut accéder aux champs et fonctions non exportés.

Considérez un package avec plusieurs fichiers :

// account/account.go
package account

type Account struct {
    balance int  // non exporté
}

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

func ResetAccount(a *Account) {
    a.balance = 0  // fonctionne - même package
}

Les deux fichiers appartiennent au package account, donc helper.go peut accéder directement au champ balance non exporté. C'est intentionnel — cela vous permet de diviser la logique du package sur plusieurs fichiers tout en conservant l'accès interne.

Cependant, le code en dehors du paquet ne peut pas accéder aux membres non exportés :

// main.go
package main

import "account"

func main() {
    acc := &account.Account{}
    acc.Deposit(100)        // fonctionne - méthode exportée
    // acc.balance = 50     // erreur - champ non exporté
    // account.ResetAccount // fonctionnerait si ResetAccount était exporté
}

Cette frontière au niveau du paquet est le mécanisme d'encapsulation principal de Go. Vous concevez les paquets comme des unités cohérentes où le code interne collabore librement, tandis que le code externe interagit uniquement via l'API exportée. Considérez chaque paquet comme un module autonome doté d'une interface publique claire.

challenge icon

Défi

Facile

Construisons un système de gestion d'inventaire qui démontre comment plusieurs fichiers au sein d'un même package peuvent collaborer en accédant directement à des champs non exportés, tout en gardant ces données cachées du code externe.

Vous allez créer trois fichiers qui travaillent ensemble dans le package main :

  • product.go : Définissez une structure Product avec un champ exporté Name et un champ non exporté quantity (int). Ajoutez une méthode exportée Info() string qui retourne le format : [Name]: [quantity] in stock
  • inventory.go : Créez des fonctions d'aide qui travaillent avec les produits en accédant directement au champ non exporté quantity (possible car ils sont dans le même package). Implémentez :
    • Restock(p *Product, amount int) - ajoute le montant à la quantité du produit
    • Sell(p *Product, amount int) bool - soustrait le montant de la quantité si le stock est suffisant, retourne true en cas de succès ou false si le stock est insuffisant
    • NewProduct(name string, initialQty int) *Product - constructeur qui crée un produit avec le nom et la quantité initiale donnés
  • main.go : Lisez les détails du produit depuis l'entrée, créez un produit en utilisant le constructeur, effectuez des opérations d'inventaire et affichez les résultats. Après chaque opération, affichez les informations du produit pour montrer l'état mis à jour.

Les entrées suivantes seront fournies :

  • Ligne 1 : Nom du produit
  • Ligne 2 : Quantité initiale (entier)
  • Ligne 3 : Montant du réapprovisionnement (entier)
  • Ligne 4 : Montant de la vente (entier)

Après avoir créé le produit, affichez ses informations initiales. Ensuite, réapprovisionnez et affichez à nouveau les informations. Enfin, tentez de vendre et affichez soit les informations mises à jour (en cas de succès), soit "Sale failed: insufficient stock" (si la quantité est insuffisante).

Par exemple, avec Laptop, 10, 5, et 8, votre sortie devrait être :

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

Et avec Phone, 3, 2, et 10, votre sortie devrait être :

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

L'idée clé ici est que inventory.go peut lire et modifier directement le champ non exporté quantity car il se trouve dans le même package que product.go. C'est l'encapsulation au niveau du package en action : la collaboration interne se fait librement tandis que les données restent protégées des packages extérieurs.

Aide-mémoire

L'encapsulation en Go fonctionne au niveau du paquet (package), et non au niveau du fichier. Tous les fichiers au sein d'un même paquet peuvent accéder aux champs et fonctions non exportés, quel que soit le fichier dans lequel ils sont définis.

Accès au niveau du paquet

Les membres non exportés sont accessibles dans tous les fichiers du même paquet :

// account/account.go
package account

type Account struct {
    balance int  // unexported
}

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

func ResetAccount(a *Account) {
    a.balance = 0  // works - same package
}

Restrictions des paquets externes

Le code situé en dehors du paquet ne peut pas accéder aux membres non exportés :

// main.go
package main

import "account"

func main() {
    acc := &account.Account{}
    acc.Deposit(100)        // works - exported method
    // acc.balance = 50     // error - unexported field
}

Cette conception vous permet de répartir la logique d'un paquet sur plusieurs fichiers tout en conservant l'accès interne, créant ainsi des unités cohérentes avec des interfaces publiques claires.

Essayez vous-même

package main

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

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

	// Lire le nom du produit
	name, _ := reader.ReadString('\n')
	name = strings.TrimSpace(name)

	// Lire la quantité initiale
	initialQtyStr, _ := reader.ReadString('\n')
	initialQty, _ := strconv.Atoi(strings.TrimSpace(initialQtyStr))

	// Lire la quantité de réapprovisionnement
	restockStr, _ := reader.ReadString('\n')
	restockAmount, _ := strconv.Atoi(strings.TrimSpace(restockStr))

	// Lire la quantité à vendre
	sellStr, _ := reader.ReadString('\n')
	sellAmount, _ := strconv.Atoi(strings.TrimSpace(sellStr))

	// TODO: Créer un nouveau produit en utilisant le constructeur NewProduct

	// TODO: Afficher les informations initiales du produit

	// TODO: Réapprovisionner le produit et afficher les informations

	// TODO: Tenter de vendre et afficher le résultat
	// Si la vente réussit, afficher les informations mises à jour
	// Si la vente échoue, afficher "Sale failed: insufficient stock"

	fmt.Println() // Remplacer par la sortie réelle
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation Orientée Objet