Help me een C++-toepassing implementeren met het Command-ontwerppatroon
description
Door het implementeren van het Command-ontwerppatroon kun je flexibelere en onderhoudbare code bereiken, en het toevoegen van nieuwe commando's vereenvoudigen zonder bestaande code te wijzigen.
prompt
try_prompt
Ik wil een C++-toepassing ontwerpen en implementeren die het Command-ontwerppatroon gebruikt om verzoeken als objecten te kapselen. Geef alstublieft begeleiding over hoe ik mijn comman ... more
generate_helper
...
tags
ai_answers
provider: openai
model: gpt-4.1-nano
Zeker! Het Command-ontwerppatroon is ideaal voor een tekstverwerker met undo/redo-functionaliteit. Hier is een stapsgewijze gids:
1. Structuur van de commando’s
- **Command Interface:** Definieer een abstracte klasse of interface `Command` met een methode zoals `execute()` en eventueel `undo()`.
- **Concrete Commando’s:** Maak klassen voor elke actie (bijvoorbeeld insert, delete, format) die deze interface implementeren. Elke klasse bevat de gegevens die nodig zijn om de actie uit te voeren en ongedaan te maken.
2. Commando-afhandelaars
- **Invoker:** Een klasse die de commando’s ontvangt en uitvoert. Bijvoorbeeld een `Editor`-klasse die commando’s aanroept.
- **Command geschiedenis:** Beheer stacks voor undo en redo. Bij het uitvoeren van een commando wordt het op de undo-stack geplaatst. Bij undo wordt het verplaatst naar de redo-stack.
3. Implementatievoorbeeld
```cpp
#include <iostream>
#include <stack>
#include <string>
// Command interface
class Command {
public:
virtual ~Command() {}
virtual void execute() = 0;
virtual void undo() = 0;
};
// Receiver: Tekstbuffer
class TextDocument {
public:
void insert(const std::string& text, size_t position) {
content.insert(position, text);
std::cout << "Ingevoegde tekst: \"" << text << "\" op positie " << position << std::endl;
}
void remove(size_t position, size_t length) {
std::string removed = content.substr(position, length);
content.erase(position, length);
std::cout << "Verwijderde tekst: \"" << removed << "\" van positie " << position << std::endl;
}
void print() const {
std::cout << "Huidige inhoud: " << content << std::endl;
}
private:
std::string content;
};
// Concrete Command: Insert
class InsertCommand : public Command {
public:
InsertCommand(TextDocument& doc, const std::string& text, size_t pos)
: document(doc), textToInsert(text), position(pos) {}
void execute() override {
document.insert(textToInsert, position);
}
void undo() override {
document.remove(position, textToInsert.length());
}
private:
TextDocument& document;
std::string textToInsert;
size_t position;
};
// Invoker
class TextEditor {
public:
void executeCommand(Command* cmd) {
cmd->execute();
undoStack.push(cmd);
// Leegmaken redoStack bij nieuw commando
while (!redoStack.empty()) {
redoStack.pop();
}
}
void undo() {
if (undoStack.empty()) {
std::cout << "Niets om ongedaan te maken.\n";
return;
}
Command* cmd = undoStack.top();
undoStack.pop();
cmd->undo();
redoStack.push(cmd);
}
void redo() {
if (redoStack.empty()) {
std::cout << "Niets om opnieuw uit te voeren.\n";
return;
}
Command* cmd = redoStack.top();
redoStack.pop();
cmd->execute();
undoStack.push(cmd);
}
private:
std::stack<Command*> undoStack;
std::stack<Command*> redoStack;
};
// Gebruik
int main() {
TextDocument doc;
TextEditor editor;
InsertCommand* insertHello = new InsertCommand(doc, "Hallo ", 0);
editor.executeCommand(insertHello);
doc.print();
InsertCommand* insertWorld = new InsertCommand(doc, "wereld!", 6);
editor.executeCommand(insertWorld);
doc.print();
std::cout << "Ongedaan maken...\n";
editor.undo();
doc.print();
std::cout << "Opnieuw uitvoeren...\n";
editor.redo();
doc.print();
// Clean-up
delete insertHello;
delete insertWorld;
return 0;
}
```
**Samenvatting:**
- Definieer een `Command` interface met `execute()` en `undo()`.
- Maak concrete commando-klassen zoals `InsertCommand`.
- Gebruik stacks voor undo/redo management.
- De `TextEditor` fungeert als de invoker die commando’s uitvoert en beheert.
Met deze structuur kun je eenvoudig nieuwe commando's toevoegen en de undo/redo functionaliteit uitbreiden.
provider: deepseek
model: deepseek-chat
Zeker! Ik zal je begeleiden bij het implementeren van het Command-ontwerppatroon voor een tekstverwerker met undo/redo-functionaliteit. Hier is een complete structuur:
## Basisstructuur
### 1. Command Interface
```cpp
// Command.h
#ifndef COMMAND_H
#define COMMAND_H
#include <string>
class Command {
public:
virtual ~Command() = default;
virtual void uitvoeren() = 0;
virtual void ongedaanMaken() = 0;
virtual std::string getBeschrijving() const = 0;
};
#endif
```
### 2. Tekstverwerker (Receiver)
```cpp
// Tekstverwerker.h
#ifndef TEKSTVERWERKER_H
#define TEKSTVERWERKER_H
#include <string>
#include <vector>
class Tekstverwerker {
private:
std::string tekst;
int cursorPositie;
public:
Tekstverwerker();
void tekstToevoegen(const std::string& toegevoegdeTekst);
void tekstVerwijderen(int startPos, int lengte);
void tekstVervangen(int startPos, int lengte, const std::string& nieuweTekst);
void setCursorPositie(int positie);
const std::string& getTekst() const;
int getCursorPositie() const;
void toonTekst() const;
};
#endif
```
```cpp
// Tekstverwerker.cpp
#include "Tekstverwerker.h"
#include <iostream>
Tekstverwerker::Tekstverwerker() : tekst(""), cursorPositie(0) {}
void Tekstverwerker::tekstToevoegen(const std::string& toegevoegdeTekst) {
tekst.insert(cursorPositie, toegevoegdeTekst);
cursorPositie += toegevoegdeTekst.length();
}
void Tekstverwerker::tekstVerwijderen(int startPos, int lengte) {
if (startPos >= 0 && startPos < tekst.length()) {
tekst.erase(startPos, lengte);
cursorPositie = startPos;
}
}
void Tekstverwerker::tekstVervangen(int startPos, int lengte, const std::string& nieuweTekst) {
tekstVerwijderen(startPos, lengte);
tekst.insert(startPos, nieuweTekst);
cursorPositie = startPos + nieuweTekst.length();
}
void Tekstverwerker::setCursorPositie(int positie) {
cursorPositie = positie;
}
const std::string& Tekstverwerker::getTekst() const {
return tekst;
}
int Tekstverwerker::getCursorPositie() const {
return cursorPositie;
}
void Tekstverwerker::toonTekst() const {
std::cout << "Huidige tekst: " << tekst << std::endl;
std::cout << "Cursor positie: " << cursorPositie << std::endl;
}
```
### 3. Concrete Commando's
```cpp
// TekstToevoegenCommando.h
#ifndef TEKSTTOEVOEGENCOMMANDO_H
#define TEKSTTOEVOEGENCOMMANDO_H
#include "Command.h"
#include "Tekstverwerker.h"
#include <string>
class TekstToevoegenCommando : public Command {
private:
Tekstverwerker* tekstverwerker;
std::string toegevoegdeTekst;
int positie;
public:
TekstToevoegenCommando(Tekstverwerker* tv, const std::string& tekst);
void uitvoeren() override;
void ongedaanMaken() override;
std::string getBeschrijving() const override;
};
#endif
```
```cpp
// TekstToevoegenCommando.cpp
#include "TekstToevoegenCommando.h"
TekstToevoegenCommando::TekstToevoegenCommando(Tekstverwerker* tv, const std::string& tekst)
: tekstverwerker(tv), toegevoegdeTekst(tekst), positie(tv->getCursorPositie()) {}
void TekstToevoegenCommando::uitvoeren() {
tekstverwerker->tekstToevoegen(toegevoegdeTekst);
}
void TekstToevoegenCommando::ongedaanMaken() {
int startPos = positie;
int lengte = toegevoegdeTekst.length();
tekstverwerker->tekstVerwijderen(startPos, lengte);
tekstverwerker->setCursorPositie(positie);
}
std::string TekstToevoegenCommando::getBeschrijving() const {
return "Tekst toevoegen: '" + toegevoegdeTekst + "'";
}
```
```cpp
// TekstVerwijderenCommando.h
#ifndef TEKSTVERWIJDERENCOMMANDO_H
#define TEKSTVERWIJDERENCOMMANDO_H
#include "Command.h"
#include "Tekstverwerker.h"
#include <string>
class TekstVerwijderenCommando : public Command {
private:
Tekstverwerker* tekstverwerker;
int startPositie;
int lengte;
std::string verwijderdeTekst;
public:
TekstVerwijderenCommando(Tekstverwerker* tv, int start, int len);
void uitvoeren() override;
void ongedaanMaken() override;
std::string getBeschrijving() const override;
};
#endif
```
```cpp
// TekstVerwijderenCommando.cpp
#include "TekstVerwijderenCommando.h"
TekstVerwijderenCommando::TekstVerwijderenCommando(Tekstverwerker* tv, int start, int len)
: tekstverwerker(tv), startPositie(start), lengte(len) {
verwijderdeTekst = tv->getTekst().substr(start, len);
}
void TekstVerwijderenCommando::uitvoeren() {
tekstverwerker->tekstVerwijderen(startPositie, lengte);
}
void TekstVerwijderenCommando::ongedaanMaken() {
tekstverwerker->tekstToevoegen(verwijderdeTekst);
tekstverwerker->setCursorPositie(startPositie);
}
std::string TekstVerwijderenCommando::getBeschrijving() const {
return "Tekst verwijderen: '" + verwijderdeTekst + "'";
}
```
### 4. Command Manager (Invoker)
```cpp
// CommandManager.h
#ifndef COMMANDMANAGER_H
#define COMMANDMANAGER_H
#include "Command.h"
#include <vector>
#include <memory>
class CommandManager {
private:
std::vector<std::unique_ptr<Command>> uitgevoerdeCommandos;
std::vector<std::unique_ptr<Command>> ongedaanGemaakteCommandos;
public:
void voerUit(std::unique_ptr<Command> commando);
void maakOngedaan();
void maakOpnieuw();
void toonGeschiedenis() const;
bool kanOngedaanMaken() const;
bool kanOpnieuwMaken() const;
};
#endif
```
```cpp
// CommandManager.cpp
#include "CommandManager.h"
#include <iostream>
void CommandManager::voerUit(std::unique_ptr<Command> commando) {
commando->uitvoeren();
uitgevoerdeCommandos.push_back(std::move(commando));
ongedaanGemaakteCommandos.clear(); // Wis redo-stack bij nieuwe actie
}
void CommandManager::maakOngedaan() {
if (!uitgevoerdeCommandos.empty()) {
auto& laatsteCommando = uitgevoerdeCommandos.back();
laatsteCommando->ongedaanMaken();
ongedaanGemaakteCommandos.push_back(std::move(laatsteCommando));
uitgevoerdeCommandos.pop_back();
}
}
void CommandManager::maakOpnieuw() {
if (!ongedaanGemaakteCommandos.empty()) {
auto& commando = ongedaanGemaakteCommandos.back();
commando->uitvoeren();
uitgevoerdeCommandos.push_back(std::move(commando));
ongedaanGemaakteCommandos.pop_back();
}
}
void CommandManager::toonGeschiedenis() const {
std::cout << "\n--- Command Geschiedenis ---" << std::endl;
std::cout << "Uitgevoerd (" << uitgevoerdeCommandos.size() << "):" << std::endl;
for (int i = 0; i < uitgevoerdeCommandos.size(); ++i) {
std::cout << i + 1 << ". " << uitgevoerdeCommandos[i]->getBeschrijving() << std::endl;
}
std::cout << "\nOngedaan gemaakt (" << ongedaanGemaakteCommandos.size() << "):" << std::endl;
for (int i = ongedaanGemaakteCommandos.size() - 1; i >= 0; --i) {
std::cout << ongedaanGemaakteCommandos.size() - i << ". "
<< ongedaanGemaakteCommandos[i]->getBeschrijving() << std::endl;
}
}
bool CommandManager::kanOngedaanMaken() const {
return !uitgevoerdeCommandos.empty();
}
bool CommandManager::kanOpnieuwMaken() const {
return !ongedaanGemaakteCommandos.empty();
}
```
### 5. Hoofdapplicatie
```cpp
// main.cpp
#include <iostream>
#include <memory>
#include "Tekstverwerker.h"
#include "CommandManager.h"
#include "TekstToevoegenCommando.h"
#include "TekstVerwijderenCommando.h"
void toonMenu() {
std::cout << "\n=== Tekstverwerker Menu ===" << std::endl;
std::cout << "1. Tekst toevoegen" << std::endl;
std::cout << "2. Tekst verwijderen" << std::endl;
std::cout << "3. Undo" << std::endl;
std::cout << "4. Redo" << std::endl;
std::cout << "5. Toon geschiedenis" << std::endl;
std::cout << "6. Toon huidige tekst" << std::endl;
std::cout << "0. Afsluiten" << std::endl;
std::cout << "Keuze: ";
}
int main() {
Tekstverwerker tekstverwerker;
CommandManager commandManager;
int keuze;
std::string invoer;
int startPos, lengte;
do {
toonMenu();
std::cin >> keuze;
std::cin.ignore(); // Clear newline
switch (keuze) {
case 1: {
std::cout << "Voer tekst in om toe te voegen: ";
std::getline(std::cin, invoer);
auto commando = std::make_unique<TekstToevoegenCommando>(&tekstverwerker, invoer);
commandManager.voerUit(std::move(commando));
break;
}
case 2: {
std::cout << "Start positie: ";
std::cin >> startPos;
std::cout << "Lengte: ";
std::cin >> lengte;
auto commando = std::make_unique<TekstVerwijderenCommando>(&tekstverwerker, startPos, lengte);
commandManager.voerUit(std::move(commando));
break;
}
case 3:
if (commandManager.kanOngedaanMaken()) {
commandManager.maakOngedaan();
std::cout << "Undo uitgevoerd." << std::endl;
} else {
std::cout << "Geen commando's om ongedaan te maken." << std::endl;
}
break;
case 4:
if (commandManager.kanOpnieuwMaken()) {
commandManager.maakOpnieuw();
std::cout << "Redo uitgevoerd." << std::endl;
} else {
std::cout << "Geen commando's om opnieuw uit te voeren." << std::endl;
}
break;
case 5:
commandManager.toonGeschiedenis();
break;
case 6:
tekstverwerker.toonTekst();
break;
case 0:
std::cout << "Afsluiten..." << std::endl;
break;
default:
std::cout << "Ongeldige keuze!" << std::endl;
}
} while (keuze != 0);
return 0;
}
```
## Belangrijke concepten:
1. **Encapsulatie**: Elk commando kapselt een actie en de informatie om deze ongedaan te maken
2. **Undo/Redo**: Twee stacks voor uitgevoerde en ongedaan gemaakte commando's
3. **Scheiding van zorgen**: CommandManager weet niet wat de commando's doen, alleen hoe ze uit te voeren
4. **Uitbreidbaarheid**: Nieuwe commando's toevoegen vereist alleen nieuwe Command subklassen
## Compilatie:
```bash
g++ -std=c++14 -o tekstverwerker main.cpp Tekstverwerker.cpp CommandManager.cpp TekstToevoegenCommando.cpp TekstVerwijderenCommando.cpp
```
Dit patroon biedt een flexibele en onderhoudbare structuur voor je tekstverwerker met robuuste undo/redo-functionaliteit!

