Pattern Command
Fa parte della sezione Programmazione orientata agli oggetti del percorso C++ di Coddy. Lezione 96 di 104.
Il pattern Command incapsula una richiesta come oggetto, consentendoti di parametrizzare i client con richieste diverse, mettere in coda le operazioni o supportare la funzionalità di annullamento. Disaccoppia l'oggetto che invoca un'operazione da quello che sa come eseguirla.
Il pattern ha quattro componenti chiave: un'interfaccia Command con un metodo execute(), Comandi concreti che implementano azioni specifiche, un Ricevitore che esegue il lavoro effettivo e un Invocatore che attiva i comandi:
#include <iostream>
#include <memory>
#include <vector>
// Ricevitore - sa come eseguire le operazioni
class Light {
public:
void turnOn() { std::cout << "Light is ON\n"; }
void turnOff() { std::cout << "Light is OFF\n"; }
};
// Interfaccia Command
class Command {
public:
virtual void execute() = 0;
virtual void undo() = 0;
virtual ~Command() = default;
};
// Comandi concreti
class LightOnCommand : public Command {
Light& light;
public:
LightOnCommand(Light& l) : light(l) {}
void execute() override { light.turnOn(); }
void undo() override { light.turnOff(); }
};
class LightOffCommand : public Command {
Light& light;
public:
LightOffCommand(Light& l) : light(l) {}
void execute() override { light.turnOff(); }
void undo() override { light.turnOn(); }
};
// Invocatore
class RemoteControl {
std::vector<std::unique_ptr<Command>> history;
public:
void pressButton(std::unique_ptr<Command> cmd) {
cmd->execute();
history.push_back(std::move(cmd));
}
void pressUndo() {
if (!history.empty()) {
history.back()->undo();
history.pop_back();
}
}
};Ogni comando memorizza un riferimento al proprio destinatario e chiama il metodo appropriato quando viene eseguito. L’invocatore non sa quale azione verrà eseguita: si limita a chiamare execute(). Memorizzando i comandi in una cronologia, puoi implementare facilmente l’annullamento chiamando il metodo undo() di ciascun comando.
Usa Command quando devi mettere in coda operazioni, implementare annulla/ripeti o separare il mittente di una richiesta dal relativo gestore.
Sfida
FacileCostruiamo un Editor di testo con funzionalità di annullamento usando il pattern Command. Creerai un sistema in cui le operazioni di modifica (come l'inserimento e l'eliminazione di testo) sono incapsulate in oggetti comando, consentendo agli utenti di eseguire azioni e poi annullarle in ordine inverso, proprio come in un vero editor di testo.
Organizzerai il codice in quattro file:
TextDocument.h: crea la classe ricevente che contiene e manipola il contenuto effettivo del testo.La classe
TextDocumentdovrebbe memorizzare il contenuto del documento come stringa. Implementa questi metodi:insertText(const std::string& text): aggiunge testo alla fine del contenutodeleteText(int count): rimuove gli ultimicountcaratteri dal contenuto (non fare nulla se count supera la lunghezza del contenuto)getContent(): restituisce il contenuto corrente come riferimento const
Command.h: definisci l'interfaccia del comando e le classi di comando concrete.Crea una classe astratta
Commandcon i metodi virtuali puriexecute()eundo(), oltre a un distruttore virtuale.Implementa quindi due comandi concreti:
InsertCommand: accetta un riferimento a unTextDocumente una stringa da inserire. Quando viene eseguito, inserisce il testo. Quando viene annullato, elimina lo stesso numero di caratteri che sono stati inseriti.DeleteCommand: accetta un riferimento a unTextDocumente il numero di caratteri da eliminare. Deve memorizzare il testo eliminato, in modo da poterlo ripristinare quando l'operazione viene annullata. Quando viene eseguito, rimuove i caratteri dalla fine. Quando viene annullato, reinserisce il testo memorizzato.
TextEditor.h: crea l'invocatore che gestisce l'esecuzione dei comandi e la cronologia.La classe
TextEditordovrebbe mantenere un riferimento a unTextDocumente una cronologia dei comandi eseguiti, usando un vector diunique_ptr<Command>. Implementa:executeCommand(std::unique_ptr<Command> cmd): esegue il comando e lo aggiunge alla cronologiaundo(): annulla il comando più recente e lo rimuove dalla cronologia (non fare nulla se la cronologia è vuota)showContent(): stampa il contenuto corrente del documento oppure[empty]se il contenuto è vuoto
main.cpp: mostra il pattern Command con funzionalità di annullamento.Leggi tre input:
- Primo testo da inserire (stringa)
- Secondo testo da inserire (stringa)
- Numero di caratteri da eliminare (intero)
Crea un
TextDocumente unTextEditor. Esegui quindi queste operazioni nell'ordine indicato, mostrando il contenuto dopo ogni passaggio:- Inserisci il primo testo, poi mostra il contenuto
- Inserisci il secondo testo, poi mostra il contenuto
- Elimina il numero di caratteri specificato, poi mostra il contenuto
- Annulla una volta, poi mostra il contenuto
- Annulla ancora una volta, poi mostra il contenuto
Per esempio, con gli input Hello, World e 3:
Hello
Hello World
Hello Wo
Hello World
HelloCon gli input Code, Editor e 6:
Code
CodeEditor
Code
CodeEditor
CodeNota come ogni comando sappia come annullare la propria operazione. DeleteCommand ricorda ciò che ha eliminato, così può ripristinarlo, e InsertCommand sa esattamente quanti caratteri rimuovere quando l'operazione viene annullata. Questa è la potenza del pattern Command: le operazioni diventano oggetti di prima classe che possono essere memorizzati, eseguiti e annullati.
Provalo tu
#include <iostream>
#include <string>
#include <memory>
#include "TextDocument.h"
#include "Command.h"
#include "TextEditor.h"
int main() {
// Leggi gli input
std::string firstText;
std::string secondText;
int deleteCount;
std::getline(std::cin, firstText);
std::getline(std::cin, secondText);
std::cin >> deleteCount;
// TODO: Crea un TextDocument e un TextEditor
// TODO: Esegui le operazioni in ordine, mostrando il contenuto dopo ciascuna:
// 1. Inserisci il primo testo, poi mostra il contenuto
// 2. Inserisci il secondo testo, poi mostra il contenuto
// 3. Elimina il numero specificato di caratteri, poi mostra il contenuto
// 4. Annulla una volta, poi mostra il contenuto
// 5. Annulla un'altra volta, poi mostra il contenuto
// Suggerimento: usa std::make_unique<InsertCommand>(...) e
// std::make_unique<DeleteCommand>(...) per creare i comandi
return 0;
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniBuild e compilazione in C++File header e file sorgenteNamespace e ambitoIntroduzione alla programmazione orientata agli oggetti in C++Classi e oggetti a confrontoIl puntatore 'this'Metodi (funzioni membro)Attributi (membri dati)Fondamenti di costruttori e distruttoriRiepilogo - Calcolatrice semplice4Proprietà delle classi
Membri di istanza e staticiGetter e setterFunzioni membro constParola chiave mutableMetodi e variabili staticiFunzioni e classi friendRiepilogo - Gestore di conti bancari7Ereditarietà
Ereditarietà di baseLivelli di accesso nell’ereditarietàOrdine di chiamata di costruttori e distruttoriRidefinizione dei metodiFunzioni virtuali e VTableEreditarietà multiplaEreditarietà virtualeRiepilogo - Gerarchia dei dipendenti2Gestione della memoria
Memoria Stack vs HeapPuntatori e riferimentiMemoria dinamica (new/delete)Puntatori intelligenti in C++RAII in C++Riepilogo - Gestore di array dinamico5Incapsulamento
Specificatori di accesso in C++Specificatori di accesso in dettaglioOccultamento delle informazioniStruct vs classClassi annidate e interneRiepilogo - Sistema di registrazione degli studenti8Polimorfismo
Polimorfismo a compile time e a runtimeOverload delle funzioniFunzioni virtuali: ripassoFunzioni virtuali pureClassi astratteProgettazione delle interfacce in C++Dynamic casting e RTTIRipasso: calcolatrice di forme11Concetti avanzati di OOP
Composizione vs ereditarietàMixin tramite CRTPIdiom PimplType ErasureEnum class e tipizzazione forteGestione delle eccezioni in OOPGerarchie personalizzate di eccezioni14Pattern di progettazione - Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeRAII come pattern3Costruttori e distruttori
Costruttore predefinitoCostruttore con parametriCostruttore di copiaCostruttore di spostamentoListe di inizializzazione dei costruttoriCostruttori delegantiApprofondimento sui distruttoriRegola del Tre / Cinque / ZeroRipasso - classe String6Sovraccarico degli operatori
Introduzione al sovraccarico degli operatoriSovraccarico degli operatori aritmeticiSovraccarico degli operatori di confrontoOperatori di flussoSovraccarico dell'operatore di assegnazioneSovraccarico degli operatori [] e ()Operatori di conversione di tipoRipasso - Classe Matrix9Template
Template di funzioneTemplate di classeSpecializzazione dei templateTemplate variadiciBasi di SFINAE e dei trait di tipoRiepilogo - Contenitore genericoEsercitati da solo: Compilatore C++ online