Masquage d'informations en Go
Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy — leçon 50 sur 107.
La dissimulation d'informations va au-delà du simple fait de rendre les champs non exportés. Il s'agit de concevoir vos types de manière à ce que le code externe ne sache que ce qu'il a besoin de savoir — rien de plus.
L'objectif est d'exposer le comportement à travers des méthodes tout en gardant la structure interne complètement cachée. Considérez cette approche :
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
}
Le code externe ne sait pas que Counter utilise un int en interne. Il pourrait s'agir d'un float64, d'une slice, ou de tout autre chose. L'implémentation peut changer sans affecter le code qui utilise Counter, tant que les méthodes se comportent de la même manière.
Ce principe s'applique également aux fonctions utilitaires. Gardez la logique interne non exportée :
// Exporté - fait partie de l'API publique
func (c *Counter) Reset() {
c.value = 0
}
// non exporté - assistant interne
func (c *Counter) isAtLimit() bool {
return c.value >= c.maxLimit
}
En masquant isAtLimit(), vous êtes libre de le modifier ou de le supprimer plus tard. Le masquage d'informations crée une frontière claire entre ce que votre package promet aux utilisateurs externes et son fonctionnement interne.
Défi
FacileConstruisons un système de portefeuille sécurisé qui démontre la dissimulation d'informations. Vous allez créer un portefeuille numérique où le suivi du solde interne et l'historique des transactions sont complètement cachés du code externe — seul le comportement est exposé via des méthodes.
Vous organiserez votre code sur deux fichiers :
wallet.go: Créez une structureWalletqui cache tout son état interne. Le portefeuille doit suivre le solde actuel et une limite de dépense en interne, mais le code externe ne doit jamais savoir comment ceux-ci sont stockés ou représentés. Exposez le comportement via ces méthodes :NewWallet(limit float64) *Wallet- constructeur qui crée un portefeuille avec la limite de dépense donnée et un solde de zéroDeposit(amount float64) bool- ajoute de l'argent au portefeuille, retournetruesi l'opération réussit (le montant doit être positif)Spend(amount float64) bool- retire de l'argent si le solde est suffisant ET que le montant ne dépasse pas la limite de dépense, retournetruesi l'opération réussitBalance() float64- retourne le solde actuelStatus() string- retourne un message d'état au format :Balance: $[balance] (Limit: $[limit])
main.go: Lisez la configuration du portefeuille et les transactions depuis l'entrée, créez un portefeuille, effectuez les opérations et affichez les résultats après chaque action.
Les entrées suivantes seront fournies :
- Ligne 1 : Limite de dépense
- Ligne 2 : Montant du dépôt
- Ligne 3 : Montant de la première dépense
- Ligne 4 : Montant de la deuxième dépense
Après avoir créé le portefeuille, affichez son état initial. Ensuite, effectuez chaque opération et affichez soit l'état mis à jour (en cas de succès), soit Transaction failed (si l'opération a été rejetée). Formatez tous les montants en dollars avec deux décimales.
Par exemple, avec 50, 100, 30 et 80, votre sortie devrait être :
Balance: $0.00 (Limit: $50.00)
Balance: $100.00 (Limit: $50.00)
Balance: $70.00 (Limit: $50.00)
Transaction failedLa dernière transaction échoue car $80 dépasse la limite de dépense de $50 — même s'il y a assez de solde. Le portefeuille applique ses règles en interne sans exposer comment il prend ces décisions. Le code externe appelle simplement des méthodes et reçoit des résultats, sans jamais connaître les détails de l'implémentation interne.
Aide-mémoire
La dissimulation d'informations consiste à concevoir des types de manière à ce que le code externe ne sache que ce qu'il a besoin de savoir. Exposez le comportement via des méthodes tout en gardant la structure interne complètement cachée.
Exemple d'un type avec une implémentation cachée :
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
}
Le code externe ne connaît pas la représentation interne (cela pourrait être un int, un float64, ou n'importe quoi d'autre). L'implémentation peut changer sans affecter le code qui l'utilise.
Gardez les fonctions d'aide non exportées :
// Exported - part of public API
func (c *Counter) Reset() {
c.value = 0
}
// unexported - internal helper
func (c *Counter) isAtLimit() bool {
return c.value >= c.maxLimit
}
La dissimulation d'informations crée une frontière claire entre ce que votre package promet à l'extérieur et la manière dont il fonctionne à l'intérieur.
Essayez vous-même
package main
import (
"fmt"
)
func main() {
// Lire les entrées
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: Créer un nouveau portefeuille avec la limite de dépenses donnée
// TODO: Afficher le statut initial
// TODO: Effectuer le dépôt et afficher le résultat (statut ou "Transaction failed")
// TODO: Effectuer la première dépense et afficher le résultat
// TODO: Effectuer la deuxième dépense et afficher le résultat
}
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
1Fondamentaux de la POO en Go
Fichiers externesEspace de travail et Modules GoPackages et ImportsNoms exportés vs non exportésIntroduction à la POO en GoLes Structs comme ClassesDéfinir des méthodes sur les StructsReceveurs par pointeur vs par valeurInitialisation de StructsFonctions constructeursRécapitulatif - Calculatrice simple4Interfaces
Introduction aux interfacesImplémentation impliciteL'interface comme contratInterface vide (any)Assertion de typeType SwitchComposition d'interfacesInterfaces Stringer & ErrorRécapitulatif - Calculateur de formes7Encapsulation
Champs exportés vs non exportésEncapsulation au niveau du packageMéthodes Getter & SetterMasquage d'informations en GoRécapitulatif - Dossiers étudiants10Génériques (Go 1.18+)
Introduction aux génériquesParamètres de typeContraintes de typeStructures génériquesContournement pour les méthodes génériquesRécapitulatif - Collections génériques13Patrons de conception, Partie 1
Introduction aux Design PatternsPatron SingletonPatron FactoryPatron Abstract FactoryPatron ObserverPatron Strategy2Exploration approfondie des Types et Structs
Types de base et compositesDéfinitions de types personnalisésTags de StructStructs anonymesStructs imbriquéesValeurs par défaut et Zero ValuesRécapitulatif - Carnet de contacts5Composition plutôt qu'héritage
Pourquoi Go n'a pas d'héritageLes bases de l'imbrication de structsPromotion de méthodesImbriquer plusieurs structsImbrication vs AgrégationMasquage de méthodes imbriquéesRécapitulatif - Hiérarchie des employés8Gestion des erreurs & POO
L'interface errorTypes d'erreurs personnalisésError Wrapping (fmt.Errorf)Erreurs sentinelleserrors.Is() et errors.As()Panic, Defer et RecoverRécapitulatif - File Parser11Bibliothèque standard et POO
io.Reader & io.WriterL'interface sort.InterfaceL'interface fmt.Stringerencoding/json avec les StructsL'interface http.HandlerRécapitulatif - Modèles d'API REST14Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Template MethodPatron ÉtatPatron CompositeMiddleware en tant que Décorateur3Pointeurs et Mémoire
Bases des pointeurs en GoPointeurs vers des structsPassage par valeur vs référenceLa fonction new()Garbage Collection en GoRécapitulatif - Constructeur de liste chaînée6Le polymorphisme en Go
Le polymorphisme via les interfacesLe Duck Typing en GoRègles de satisfaction des interfacesCollections polymorphesInjection de dépendancesRécapitulatif - Processeur de paiement9Concurrence et POO
Bases des GoroutinesChannels et CommunicationChannels avec buffer vs sans bufferL'instruction Selectsync.Mutex et sync.RWMutexsync.WaitGroupConception de structures Thread-SafeRécapitulatif - Worker Pool