Dissimulation de l’information en Go
Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy. Leçon 50 sur 107.
La dissimulation de l’information 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 connaisse que ce qu’il a besoin de connaître. Rien de plus.
L’objectif est d’exposer le comportement par l’intermédiaire de 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 tranche ou de quelque chose de complètement différent. 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é - partie de l'API publique
func (c *Counter) Reset() {
c.value = 0
}
// non exporté - helper interne
func (c *Counter) isAtLimit() bool {
return c.value >= c.maxLimit
}
En masquant isAtLimit(), vous êtes libre de le modifier ou de le supprimer ultérieurement. La dissimulation des informations crée une limite claire entre ce que votre paquet promet aux utilisateurs externes et son fonctionnement interne.
Défi
FacileConstruisons un système de portefeuille sécurisé qui démontre la dissimulation des informations. Vous allez créer un portefeuille numérique dont le suivi interne du solde et l’historique des transactions sont complètement masqués du code externe : seul le comportement est exposé par l’intermédiaire de méthodes.
Vous organiserez votre code sur deux fichiers :
wallet.go: créez une structureWalletqui masque tout son état interne. Le portefeuille doit suivre en interne le solde actuel et une limite de dépenses, mais le code externe ne doit jamais savoir comment ces données sont stockées ou représentées. Exposez le comportement au moyen de ces méthodes :NewWallet(limit float64) *Wallet- constructeur qui crée un portefeuille avec la limite de dépenses donnée et un solde nulDeposit(amount float64) bool- ajoute de l’argent au portefeuille et renvoietrueen cas de réussite (le montant doit être positif)Spend(amount float64) bool- retire de l’argent si le solde est suffisant ET si le montant ne dépasse pas la limite de dépenses, puis renvoietrueen cas de réussiteBalance() float64- renvoie le solde actuelStatus() string- renvoie un message d’état au format suivant :Balance: $[balance] (Limit: $[limit])
main.go: lisez la configuration du portefeuille et les transactions à partir de 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épenses
- Ligne 2 : montant du dépôt
- Ligne 3 : premier montant dépensé
- Ligne 4 : deuxième montant dépensé
Après avoir créé le portefeuille, affichez son état initial. Effectuez ensuite chaque opération et affichez soit l’état mis à jour (en cas de réussite), soit Transaction failed (si l’opération a été refusée). Formatez tous les montants en dollars avec deux chiffres après la virgule.
Par exemple, avec 50, 100, 30 et 80, votre sortie doit ê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 parce que 80 $ dépasse la limite de dépenses de 50 $, même si le solde est suffisant. Le portefeuille applique ses règles en interne sans révéler comment il prend ces décisions. Le code externe appelle simplement les méthodes et reçoit les résultats, sans jamais connaître les détails de l’implémentation interne.
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: Perform deposit and print result (status or "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 et non exportésIntroduction à la POO en GoStructs comme classesDéfinir des méthodes sur des structsRécepteurs pointeurs ou par valeurInitialisation des structsFonctions constructeursRécapitulatif - Calculatrice simple4Interfaces
Introduction aux interfacesImplémentation impliciteL’interface comme contratInterface vide (any)Assertion de typeCommutation de typeComposition d’interfacesInterfaces Stringer et ErrorRécapitulatif - Calculateur de formes7Encapsulation
Champs exportés vs non exportésEncapsulation au niveau du packageMéthodes Getter et SetterDissimulation de l’information en GoRécapitulatif – Fiches d’étudiants10Génériques (Go 1.18+)
Introduction aux génériquesParamètres de typeContraintes de typeStructures génériquesSolution de contournement pour les méthodes génériquesRécapitulatif - Collection générique2Plongée approfondie dans les types et les structs
Types de base et compositesDéfinitions de types personnalisésTags de structStructs anonymesStructs imbriquésValeurs zéro et valeurs par défautRécapitulatif - Carnet de contacts5La composition plutôt que l’héritage
Pourquoi Go n’a pas d’héritageBases de l’inclusion de structsPromotion des méthodesInclure plusieurs structsInclusion ou agrégationMasquage des méthodes inclusesRécapitulatif : hiérarchie des employés8Gestion des erreurs et POO
L’interface errorTypes d’erreurs personnalisésEnrobage des erreurs (fmt.Errorf)Erreurs sentinelleserrors.Is() et errors.As()Panic, Defer et RecoverRécapitulatif - Analyseur de fichiers11Bibliothèque standard et POO
io.Reader et io.Writersort.InterfaceInterface fmt.Stringerencoding/json avec des structsInterface http.HandlerRécapitulatif - Modèles d’API REST14Modèles de conception – Partie 2
Modèle CommandeModèle AdaptateurModèle DécorateurModèle Méthode modèleModèle ÉtatModèle CompositeMiddleware comme décorateur3Pointeurs et mémoire
Notions de base des pointeurs en GoPointeurs vers des structuresPassage par valeur ou par référenceLa fonction new()Garbage collection en GoRécapitulatif - Constructeur de listes chaînées6Polymorphisme en Go
Polymorphisme via les interfacesDuck typing en GoRègles de satisfaction des interfacesCollections polymorphesInjection de dépendancesRécapitulatif – Processeur de paiements9Concurrence et POO
Bases des GoroutinesCanaux et communicationCanaux tamponnés ou non tamponnésInstruction selectsync.Mutex et sync.RWMutexsync.WaitGroupConception de structures thread-safeRécapitulatif - Pool de workersEntraînez-vous par vous-même : Compilateur Go en ligne