io.Reader et io.Writer
Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy. Leçon 73 sur 107.
La bibliothèque standard de Go met en valeur une conception basée sur les interfaces grâce à deux interfaces fondamentales : io.Reader et io.Writer. Ces interfaces simples permettent tout, des opérations sur les fichiers aux communications réseau.
L’interface io.Reader ne requiert qu’une seule méthode :
type Reader interface {
Read(p []byte) (n int, err error)
}Il lit jusqu’à len(p) octets dans la tranche et renvoie le nombre d’octets lus. De même, io.Writer définit :
type Writer interface {
Write(p []byte) (n int, err error)
}Tout type implémentant ces méthodes fonctionne avec l’ensemble de l’écosystème des E/S.
Voici un type personnalisé qui implémente io.Reader :
type RepeatReader struct {
Char byte
Count int
pos int
}
func (r *RepeatReader) Read(p []byte) (int, error) {
if r.pos >= r.Count {
return 0, io.EOF
}
n := 0
for n < len(p) && r.pos < r.Count {
p[n] = r.Char
n++
r.pos++
}
return n, nil
}Maintenant, ce lecteur personnalisé fonctionne avec toute fonction qui attend un io.Reader :
reader := &RepeatReader{Char: 'A', Count: 5}
data, _ := io.ReadAll(reader)
fmt.Println(string(data)) // AAAAACette conception vous permet d’écrire des fonctions qui acceptent io.Reader ou io.Writer, ce qui les rend compatibles avec des fichiers, des connexions réseau, des tampons ou toute implémentation personnalisée.
Défi
FacileConstruisons un processeur de flux de texte personnalisé qui implémente les interfaces io.Reader et io.Writer ! Vous allez créer des types capables de transformer le texte au fur et à mesure qu’il les traverse, en montrant comment ces interfaces fondamentales permettent une composition puissante des opérations d’E/S.
Vous organiserez votre code sur deux fichiers :
streams.go: définissez vos types personnalisés de lecteur et d’écrivain.Créez une structure
CountingReaderqui encapsule une chaîne et suit le nombre d’octets lus. Elle doit contenir des champs pour la chaîne source et un indicateur de position. Implémentez la méthodeRead(p []byte) (int, error)qui lit les octets de la source dans la tranche fournie, met à jour la position et renvoieio.EOFlorsque la source est épuisée.Créez une structure
UppercaseWriterqui collecte les données écrites et les convertit en majuscules. Elle doit stocker le résultat accumulé en interne. Implémentez la méthodeWrite(p []byte) (int, error)qui convertit les octets reçus en majuscules et les stocke. Ajoutez une méthodeResult() stringpour récupérer le texte accumulé en majuscules.Créez les fonctions constructrices
NewCountingReader(source string) *CountingReaderetNewUppercaseWriter() *UppercaseWriterafin d’initialiser correctement vos types.main.go: montrez vos types d’E/S personnalisés en fonctionnement avec la bibliothèque standard.Lisez une chaîne en entrée, puis utilisez votre
CountingReaderavecio.ReadAllpour lire toutes les données. Faites passer ces données dans votreUppercaseWriterà l’aide de sa méthodeWrite.Affichez les résultats dans ce format :
Original: [input] Uppercase: [result from writer] Bytes read: [total bytes]
L’entrée suivante sera fournie :
- Une seule ligne de texte
Par exemple, avec :
Hello WorldVotre sortie doit être :
Original: Hello World
Uppercase: HELLO WORLD
Bytes read: 11Et avec :
Go interfaces are powerfulVotre sortie doit être :
Original: Go interfaces are powerful
Uppercase: GO INTERFACES ARE POWERFUL
Bytes read: 26L’idée essentielle est que votre CountingReader fonctionne parfaitement avec io.ReadAll parce qu’il implémente io.Reader. Toute fonction de l’écosystème Go qui accepte un io.Reader fonctionnera avec votre type personnalisé : c’est toute la puissance d’une conception fondée sur les interfaces.
Essayez vous-même
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
// Lire l'entrée
reader := bufio.NewReader(os.Stdin)
input, _ := reader.ReadString('\n')
// Supprimer le saut de ligne final s'il est présent
if len(input) > 0 && input[len(input)-1] == '\n' {
input = input[:len(input)-1]
}
// TODO: Créer un CountingReader avec la chaîne d'entrée
// countingReader := NewCountingReader(input)
// TODO: Utiliser io.ReadAll pour lire toutes les données de votre CountingReader
// data, err := io.ReadAll(countingReader)
// TODO: Créer un UppercaseWriter
// upperWriter := NewUppercaseWriter()
// TODO: Écrire les données dans votre UppercaseWriter
// upperWriter.Write(data)
// TODO: Afficher les résultats dans le format requis :
// Original: [input]
// Uppercase : [résultat du writer]
// Bytes read : [total d'octets]
// Placeholder pour éviter l'erreur d'import non utilisé - à supprimer lors de l'implémentation
_ = io.EOF
fmt.Println("Original:", input)
// fmt.Println("Uppercase:", upperWriter.Result())
// fmt.Println("Bytes read:", countingReader.Position)
}
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