Modèle Commande
Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy. Leçon 91 sur 107.
Le patron Command encapsule une requête sous forme d’objet, ce qui vous permet de paramétrer des opérations, de les mettre en file d’attente ou de prendre en charge la fonctionnalité d’annulation. Alors que Strategy encapsule des algorithmes, Command encapsule des actions entières avec leurs paramètres.
En Go, nous définissons une interface Command avec une méthode Execute, puis créons des commandes concrètes qui contiennent toutes les informations nécessaires pour effectuer une action :
type Command interface {
Execute() string
}
type Light struct {
IsOn bool
}
type TurnOnCommand struct {
light *Light
}
func (c *TurnOnCommand) Execute() string {
c.light.IsOn = true
return "Light turned on"
}
type TurnOffCommand struct {
light *Light
}
func (c *TurnOffCommand) Execute() string {
c.light.IsOn = false
return "Light turned off"
}Un invocateur stocke et exécute des commandes sans savoir ce qu’elles font :
type RemoteControl struct {
command Command
}
func (r *RemoteControl) SetCommand(c Command) {
r.command = c
}
func (r *RemoteControl) PressButton() string {
return r.command.Execute()
}L’invocateur est complètement découplé du récepteur (la lumière) :
light := &Light{}
remote := &RemoteControl{}
remote.SetCommand(&TurnOnCommand{light: light})
fmt.Println(remote.PressButton()) // Lumière allumée
remote.SetCommand(&TurnOffCommand{light: light})
fmt.Println(remote.PressButton()) // Lumière éteinteCommand est idéal pour implémenter des systèmes d’annulation/rétablissement, des files d’attente de tâches ou l’enregistrement de macros lorsque vous devez stocker, différer ou rejouer des opérations.
Défi
FacileConstruisons un éditeur de texte avec une fonctionnalité d’annulation en utilisant le pattern Command ! Tu vas créer des commandes qui encapsulent les opérations sur le texte, permettant à l’éditeur d’exécuter des actions et de les annuler : un cas d’utilisation classique qui montre pourquoi les commandes sont si puissantes.
Tu vas organiser ton code sur trois fichiers :
command.go: définis ton interface de commande et les commandes concrètes d’édition de texte.Crée une interface
Commandavec deux méthodes :Execute() stringpour effectuer l’action etUndo() stringpour l’annuler.Implémente deux types de commandes qui opèrent sur un
*TextEditor:InsertCommand: contient le pointeur vers l’éditeur et le texte à insérer. Execute ajoute le texte au contenu de l’éditeur et renvoieInserted: [text]. Undo supprime ce texte de la fin et renvoieUndone insert: [text]DeleteCommand: contient le pointeur vers l’éditeur et un nombre de caractères à supprimer à la fin. Execute supprime ces caractères (en les conservant pour l’annulation) et renvoieDeleted: [removed text]. Undo les restaure et renvoieUndone delete: [restored text]
editor.go: crée ton éditeur de texte (le récepteur) et l’invocateur qui gère l’historique des commandes.Construis une structure
TextEditoravec un champContent(string) et des méthodesAppend(text string)etDeleteLast(count int) string(qui renvoie le texte supprimé).Construis une structure
EditorInvokerqui contient une tranche de commandes exécutées servant d’historique. Ajoute ces méthodes :ExecuteCommand(cmd Command) string: exécute la commande, l’ajoute à l’historique et renvoie le résultatUndoLast() string: supprime la dernière commande de l’historique, appelle sa méthode Undo et renvoie le résultat. Si l’historique est vide, renvoieNothing to undo
main.go: montre ton système de commandes avec une série d’opérations.Lis un nombre d’opérations. Pour chaque opération, lis le type d’opération (
insert,deleteouundo). Pour insert, lis également le texte à insérer. Pour delete, lis le nombre de caractères. Exécute chaque opération via ton invocateur et affiche le résultat. Après toutes les opérations, affiche le contenu final de l’éditeur sous la formeFinal: [content].
Les entrées suivantes seront fournies :
- Ligne 1 : nombre d’opérations
- Pour chaque opération : type d’opération, puis les données supplémentaires si nécessaire (texte pour insert, nombre pour delete)
Par exemple, avec :
5
insert
Hello
insert
World
delete
3
undo
undoTa sortie doit être :
Inserted: Hello
Inserted: World
Deleted: rld
Undone delete: rld
Undone insert: World
Final: HelloEt avec :
4
insert
Go
insert
Lang
undo
insert
!Ta sortie doit être :
Inserted: Go
Inserted: Lang
Undone insert: Lang
Inserted: !
Final: Go!Et avec :
2
undo
insert
TestTa sortie doit être :
Nothing to undo
Inserted: Test
Final: TestRemarque comment chaque commande encapsule tout ce qui est nécessaire pour effectuer et annuler son action. L’invocateur ne sait pas ce que font les commandes. Il les exécute simplement et maintient l’historique pour permettre l’annulation !
Essayez vous-même
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Lire le nombre d'opérations
line, _ := reader.ReadString('\n')
numOps, _ := strconv.Atoi(strings.TrimSpace(line))
// Créer l'editor et l'invoker
editor := &TextEditor{}
invoker := &EditorInvoker{}
for i := 0; i < numOps; i++ {
// Lire le type d'opération
opLine, _ := reader.ReadString('\n')
opType := strings.TrimSpace(opLine)
var result string
switch opType {
case "insert":
// Lire le texte à insérer
textLine, _ := reader.ReadString('\n')
text := strings.TrimSuffix(textLine, "\n")
// TODO: Créer un InsertCommand et l'exécuter via l'invoker
_ = text
_ = editor
case "delete":
// Lire le nombre de caractères à supprimer
countLine, _ := reader.ReadString('\n')
count, _ := strconv.Atoi(strings.TrimSpace(countLine))
// TODO: Créer un DeleteCommand et l'exécuter via l'invoker
_ = count
case "undo":
// TODO: Appeler UndoLast sur l'invoker
}
fmt.Println(result)
}
// Afficher le contenu final
fmt.Printf("Final: %s\n", editor.Content)
}
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