Modèle Adaptateur
Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy. Leçon 92 sur 107.
Le patron Adaptateur permet à des interfaces incompatibles de fonctionner ensemble en enveloppant un type existant avec une nouvelle interface. Alors que Command encapsule des actions, Adaptateur traduit une interface en une autre que les clients attendent.
Imaginez que vous avez une ancienne imprimante qui utilise une signature de méthode différente de celle attendue par votre application :
// Interface cible utilisée par votre application
type Printer interface {
Print(text string) string
}
// Imprimante héritée avec une interface incompatible
type OldPrinter struct{}
func (o OldPrinter) PrintDocument(doc string, copies int) string {
return fmt.Sprintf("Old printer: %s (x%d)", doc, copies)
}L’adaptateur encapsule le type hérité et implémente l’interface attendue :
type OldPrinterAdapter struct {
oldPrinter OldPrinter
}
func (a OldPrinterAdapter) Print(text string) string {
return a.oldPrinter.PrintDocument(text, 1)
}Désormais, l’ancienne imprimante fonctionne parfaitement avec le code qui attend l’interface Printer :
func PrintMessage(p Printer, msg string) string {
return p.Print(msg)
}
adapter := OldPrinterAdapter{oldPrinter: OldPrinter{}}
result := PrintMessage(adapter, "Hello") // "Old printer: Hello (x1)"Le modèle Adapter est précieux lors de l’intégration de bibliothèques tierces, de l’utilisation de code ancien ou lorsque vous devez utiliser des classes existantes qui ne correspondent pas aux exigences de votre interface. Il agit comme un pont sans modifier le code d’origine.
Défi
FacileConstruisons un système de lecteur multimédia à l’aide du modèle Adapter ! Vous disposez d’un lecteur audio moderne qui attend une interface unifiée, mais vous devez intégrer un équipement audio ancien qui utilise des signatures de méthodes complètement différentes. Votre adaptateur fera le lien, permettant à l’ancien matériel de fonctionner parfaitement avec le nouveau système.
Vous organiserez votre code sur trois fichiers :
player.go: définissez votre interface cible et un appareil audio ancien.Créez une interface
AudioPlayerque votre système moderne attend, avec une méthodePlay(track string) stringqui prend le nom d’une piste et renvoie un message de lecture.Définissez également un appareil ancien qui ne correspond pas à cette interface :
- la structure
VinylPlayeravec un champSpeedRPM(int). Elle possède une méthodeDropNeedle(record string, side string) stringqui renvoiePlaying [record] ([side] side) at [speed]rpm
L’interface de VinylPlayer est incompatible avec AudioPlayer. Elle nécessite deux paramètres et utilise un nom de méthode complètement différent.
- la structure
adapter.go: créez votre adaptateur afin de rendre l’ancien appareil compatible.Construisez une structure
VinylAdapterqui encapsule unVinylPlayeret implémente l’interfaceAudioPlayer. LorsquePlayest appelée, l’adaptateur doit appeler la méthodeDropNeedledu lecteur de vinyles, en utilisant par défautApour le paramètre de face.Créez également un lecteur moderne à titre de comparaison :
- structure
DigitalPlayer: sa méthodePlayrenvoieStreaming: [track]
- structure
main.go: démontrez le fonctionnement des deux lecteurs via la même interface.Créez une fonction
PlayMusic(player AudioPlayer, track string) stringqui utilise l’interface pour lire de la musique. Cette fonction ne doit pas savoir ni se soucier de savoir si elle utilise un lecteur numérique ou un lecteur de vinyles adapté.Lisez un type de lecteur (
digitalouvinyl) et, s’il s’agit de vinyles, lisez également la vitesse en RPM. Lisez ensuite le nom de la piste. Créez le lecteur approprié (en utilisant l’adaptateur pour les vinyles), lisez la piste via votre fonctionPlayMusicet affichez le résultat.
Les entrées suivantes seront fournies :
- Ligne 1 : type de lecteur (
digitalouvinyl) - Ligne 2 : vitesse en RPM (uniquement pour les vinyles)
- Dernière ligne : nom de la piste à lire
Par exemple, avec :
digital
Bohemian RhapsodyVotre sortie doit être :
Streaming: Bohemian RhapsodyEt avec :
vinyl
33
Abbey RoadVotre sortie doit être :
Playing Abbey Road (A side) at 33rpmEt avec :
vinyl
45
Blue MondayVotre sortie doit être :
Playing Blue Monday (A side) at 45rpmRemarquez comment la fonction PlayMusic fonctionne de manière identique avec les deux lecteurs. Elle ne sait pas que le lecteur de vinyles est en réalité adapté à partir d’une interface complètement différente. L’adaptateur traduit en arrière-plan les appels à l’interface moderne au format ancien !
Essayez vous-même
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// PlayMusic utilise l'interface AudioPlayer pour jouer de la musique
// Cette fonction fonctionne avec n'importe quelle implémentation d'AudioPlayer
// TODO: Implémenter cette fonction pour appeler player.Play(track) et retourner le résultat
func PlayMusic(player AudioPlayer, track string) string {
// TODO: Implémenter cette fonction
return ""
}
func main() {
reader := bufio.NewReader(os.Stdin)
// Lire le type de lecteur
playerType, _ := reader.ReadString('\n')
playerType = strings.TrimSpace(playerType)
var player AudioPlayer
var track string
if playerType == "vinyl" {
// Lire la vitesse RPM pour le vinyle
rpmStr, _ := reader.ReadString('\n')
rpmStr = strings.TrimSpace(rpmStr)
rpm, _ := strconv.Atoi(rpmStr)
// Lire le nom de la piste
track, _ = reader.ReadString('\n')
track = strings.TrimSpace(track)
// TODO: Créer un VinylPlayer avec le RPM donné
// TODO: L'envelopper dans un VinylAdapter
// TODO: Assigner à la variable player
_ = rpm // Supprimer cette ligne lorsque vous utilisez rpm
} else {
// Lire le nom de la piste
track, _ = reader.ReadString('\n')
track = strings.TrimSpace(track)
// TODO: Créer un DigitalPlayer
// TODO: Assigner à la variable player
}
// TODO: Appeler PlayMusic et afficher le résultat
_ = player // Supprimer cette ligne lorsque vous utilisez player
_ = track // Supprimer cette ligne lorsque vous utilisez track
}
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