C++模拟实现撤销和重做功能

C++模拟实现撤销和重做功能

  • 概述
  • 基本思路
  • 效果预览
  • 代码实现

概述

此功能是我在学习设计模式的时候根据其中的命令模式的原理写出来的一个demo,原理应该和各种编辑器上的redo/undo(重做/撤销)是一样的,如果不知道命令模式的运行逻辑,可以先去菜鸟教程上看一下它的原理实现再来查看此例程。

链接: 命令模式|菜鸟教程.

另外一个网站有比较详尽的原理介绍:

链接: 命令模式原理介绍.

基本思路

我们平时写代码的时候所产生的命令是一种强耦合的命令形式,简单的来说就是下达命令然后立即执行的形式,这种强耦合的方法使得执行过的命令具有不可回溯性(执行过的命令难以留存下来),并且执行命令的时机不能灵活处理。
命令模式为了解决上述问题,提出了松耦合的机制,简单来讲强耦合的命令形式有两个对象,命令发出者和命令执行者,而命令模式在命令发出者和执行者之间添加了一个记录命令的对象。有了这个方法对象以后系统就可以很方便的对执行过的命令进行回溯处理或者集中统一处理了。

如果说上面的描述让你感到困惑的话,不妨看一下这个例子:

假如你在市中心开了一家餐馆,刚开始的时候店小人不多,所以只需要一个厨师就能处理所有的事情(点餐和执行)(强耦合),但是后来餐馆小有名气,客人越来越多,强耦合的弊端就开始显现出来了,越来越多的点餐命令让厨师忙不过来,经常发生先进来的顾客等不到餐,后进来的顾客很快得到处理的事情。而在这种情况下,命令模式的解决方法就是:再招一个服务生,服务生通过便签纸的方式将客人的订单记录在案并且通过先进先出的方式对订单进行排序,这样就实现了一个命令的松耦合,厨师不仅可以正确的按照订单的先后顺序执行命令,也可以等待订单数量足够多了以后再不慌不慢地进行统一处理,甚至可以将执行过的订单丢到一个栈里面去,方便命令的回溯处理。

效果预览

C++模拟实现撤销和重做功能_第1张图片
这是一个模拟股票买卖的程序。
第一行为已经执行过的步骤,第二行提示按序号键入命令,括号内的内容为现在持有的股票数量,三四行为具体命令。

代码实现

下面是代码实现部分,开两个线程的目的是我想让主线程只负责命令的入队,子线程负责处理命令。

redo.h

#pragma once
#ifndef _REDO_H
#define _REDO_H

#include 
#include 
#include 
#include 
#include 
#include 
using namespace::std;

//创建一个命令接口
class Order {
public:
    virtual int execute(bool flag) {
        cout << "错误:抽象类的execute方法被调用了" << endl;
        return 0;
    }
    virtual int redo(bool flag) = 0;
};

//创建一个请求类
class Stock {
private:
    static string name;
    static int quantity;
public:
    Stock() {}
    //三种购买方案
    int buyten(bool flag);
    int buysixty(bool flag);
    int buyhundred(bool flag);

    //三种抛售方案
    int sellten(bool flag);
    int sellsixty(bool flag);
    int sellhundred(bool flag);

    int showHeldnum() {
        return quantity;
    }
};

//创建实现了 Order 接口的实体类。
class BuytenStock :public Order {
private:
    Stock* abcStock;
public:
    BuytenStock(Stock* Stock) :abcStock(Stock){}

    int execute(bool flag);
    int redo(bool flag);
};

class BuysixtyStock :public Order {
private:
    Stock* abcStock;
public:
    BuysixtyStock(Stock* Stock) :abcStock(Stock) {}

    int execute(bool flag);
    int redo(bool flag);
};

class BuyhundredStock :public Order {
private:
    Stock* abcStock;
public:
    BuyhundredStock(Stock* Stock) :abcStock(Stock) {}

    int execute(bool flag);
    int redo(bool flag);
};

class SelltenStock :public Order {
private:
    Stock* abcStock;
public:
    SelltenStock(Stock* Stock) :abcStock(Stock) {}

    int execute(bool flag);
    int redo(bool flag);
};

class SellsixtyStock :public Order {
private:
    Stock* abcStock;
public:
    SellsixtyStock(Stock* Stock) :abcStock(Stock) {}

    int execute(bool flag);
    int redo(bool flag);
};

class SellhundredStock :public Order {
private:
    Stock* abcStock;
public:
    SellhundredStock(Stock* Stock) :abcStock(Stock) {}

    int execute(bool flag);
    int redo(bool flag);
};

//创建命令调用类。
class Broker {
private:
    queue<Order*> orderList;
    stack<Order*> redoStack;//存放已经执行过的命令(还原/撤销操作)
    stack<Order*> undoStack;//存放执行过的撤销命令(撤销还原)
    stack<Order*> cacheStack;//减小redo栈空间时使用的缓存栈
public:
    //命令入队
    void takeOrder(Order* order);
    //按队列顺序执行命令
    void placeOrders();
    //撤销
    int redoOrders();
    //撤销还原
    int undoOrders();

    void clearRedoStack(unsigned int num);
    void clearUndoStack();
};

extern Stock* abcStock;//执行函数(厨师)

extern Order* buytenStockOrder;//方法对象(食物订单)
extern Order* buysixtyStockOrder;
extern Order* buyhundredStockOrder;
extern Order* selltenStockOrder;
extern Order* sellsixtyStockOrder;
extern Order* sellhundredStockOrder;

extern Broker* broker;//方法管理者(服务生)

extern int buf[50];

void _thread();

void relese();

string printData(int val);

#endif _REDO_H

redo.cpp

#include "redo.h"

Stock* abcStock = new Stock();

Order* buytenStockOrder = (Order*)new BuytenStock(abcStock);
Order* buysixtyStockOrder = (Order*)new BuysixtyStock(abcStock);
Order* buyhundredStockOrder = (Order*)new BuyhundredStock(abcStock);

Order* selltenStockOrder = (Order*)new SelltenStock(abcStock);
Order* sellsixtyStockOrder = (Order*)new SellsixtyStock(abcStock);
Order* sellhundredStockOrder = (Order*)new SellhundredStock(abcStock);

Broker* broker = new Broker();

int buf[50] = {0};

//静态成员类外初始化
int Stock::quantity = 0;
string Stock::name = "ABC";

//打印当前流程函数,每执行Stock类的一个方法最终都会返回一个Int值
//根据传入Int值的大小来判断进行了什么操作,然后将其记录到一个数组
//里,需要查看当前执行操作时,只需要遍历数组,将其含义转换成字符串
//返回即可
string printData(int val) {
	string str;
	int i = 0;
	int num = 0;
	while (buf[num]) {
		if (buf[num] == 0)break;
		++num;
	}
	//如果buf最后一位返回值为-1,表示刚执行完redo操作
	//需要删除buf里的最后一步操作和-1值
	if (buf[num - 1] == -1) {
		buf[num - 1] = 0;
		buf[num - 2] = 0;
	}
	//遍历数组并输出执行步骤字符串
	if (val == 0) {
		if (buf[0] == 0) return "no step excuted";
		while (buf[i]) {
			if (buf[i] == 0) break;
			switch (buf[i]) {
			case 10:str += "buy 10->"; break;
			case 60:str += "buy 60->"; break;
			case 100:str += "buy 100->"; break;
			case -10:str += "sell 10->"; break;
			case -60:str += "sell 60->"; break;
			case -100:str += "sell 100->"; break;
			default: break;
			}
			++i;
		}
		str += "NULL";
		return str;
	}
	//向数组中添加新元素
	if (val != 0) {
		buf[num] = val;
		return "no print model!";
	}
	return "printData err!";
}

//子线程执行函数,主线程负责命令入队,子线程负责处理命令
void _thread() {
	while (1) {
		Sleep(300);
		broker->clearRedoStack(10);
		broker->placeOrders();
	}
}

//Stock类提供的三种购买方案
int Stock::buyten(bool flag) {
	quantity += 10;
	if(flag == true)
	cout << "Stock [ Name:" << name << ",Quantity:" << 10 << " ] bought" << endl;
	return 10;
}
int Stock::buysixty(bool flag) {
	quantity += 60;
	if (flag == true)
	cout << "Stock [ Name:" << name << ",Quantity:" << 60 << " ] bought" << endl;
	return 60;
}
int Stock::buyhundred(bool flag) {
	quantity += 100;
	if (flag == true)
	cout << "Stock [ Name:" << name << ",Quantity:" << 100 << " ] bought" << endl;
	return 100;
}

//Stock类提供的三种抛售方案
int Stock::sellten(bool flag) {
	quantity -= 10;
	if (flag == true)
	cout << "Stock [ Name:" << name << ",Quantity:" << 10 << " ] sold" << endl;
	return -10;
}
int Stock::sellsixty(bool flag) {
	quantity -= 60;
	if (flag == true)
	cout << "Stock [ Name:" << name << ",Quantity:" << 60 << " ] sold" << endl;
	return -60;
}
int Stock::sellhundred(bool flag) {
	quantity -= 100;
	if (flag == true)
	cout << "Stock [ Name:" << name << ",Quantity:" << 100 << " ] sold" << endl;
	return -100;
}

//方法对象的三个买方法和三个卖方法
int BuytenStock::execute(bool flag)
{
	return abcStock->buyten(flag);
}
int BuysixtyStock::execute(bool flag)
{
	return abcStock->buysixty(flag);
}
int BuyhundredStock::execute(bool flag)
{
	return abcStock->buyhundred(flag);
}

int SelltenStock::execute(bool flag) {
	return abcStock->sellten(flag);
}
int SellsixtyStock::execute(bool flag) {
	return abcStock->sellsixty(flag);
}
int SellhundredStock::execute(bool flag) {
	return abcStock->sellhundred(flag);
}

//六种买卖方法的撤销操作
int BuytenStock::redo(bool flag) {
	return abcStock->sellten(flag);
	cout << "动作已撤销" << endl;
}
int BuysixtyStock::redo(bool flag) {
	return abcStock->sellsixty(flag);
	cout << "动作已撤销" << endl;
}
int BuyhundredStock::redo(bool flag) {
	return abcStock->sellhundred(flag);
	cout << "动作已撤销" << endl;
}
int SelltenStock::redo(bool flag) {
	return abcStock->buyten(flag);
	cout << "动作已撤销" << endl;
}
int SellsixtyStock::redo(bool flag) {
	return abcStock->buysixty(flag);
	cout << "动作已撤销" << endl;
}
int SellhundredStock::redo(bool flag) {
	return abcStock->sellhundred(flag);
	cout << "动作已撤销" << endl;
}

void Broker::takeOrder(Order* order) {
	orderList.push(order);
}
void Broker::placeOrders() {
	while (!orderList.empty()) {
		//执行队头命令,printData:将执行命令的返回值
		//加入int buf(记录操作步骤)
		printData(orderList.front()->execute(true));
		//刚执行过的命令入栈
		redoStack.push(orderList.front());
		//弹出队头元素
		orderList.pop();
	}
}

int Broker::redoOrders() {
	if (redoStack.empty()) {
		cout << "没有操作可撤销!" << endl;
		return 0;
	}
	redoStack.top()->redo(false);
	//执行过的撤销命令进入undo(撤销还原)栈
	undoStack.push(redoStack.top());

	redoStack.pop();

	return -1;
}

int Broker::undoOrders() {
	int val = 0;
	if (undoStack.empty()) {
		cout << "无法执行撤销还原!" << endl;
		cout << "原因:1.新的操作覆盖 2.没有撤销还原操作可执行" << endl;
		return 0;
	}
	val = undoStack.top()->execute(false);
	//执行过的撤销还原命令进入redo(撤销)栈
	redoStack.push(undoStack.top());

	undoStack.pop();

	return val;
}

//清除redo(撤销)栈多余元素,只留下num个
void Broker::clearRedoStack(unsigned int num) {
	//如果栈内元素小于num,则不需要清除
	if (redoStack.size() <= num) {
		return;
	}
	//把栈内元素全部倒入cacheStack
	while (!redoStack.empty()) {
		cacheStack.push(redoStack.top());
		redoStack.pop();
	}
	//清除陈旧的redo记录
	while (cacheStack.size() > num) {
		cacheStack.pop();
	}
	//把cacheStack中剩余的num条元素倒回redoStack
	while (!cacheStack.empty()) {
		redoStack.push(cacheStack.top());
		cacheStack.pop();
	}
}

void Broker::clearUndoStack() {
	while (!undoStack.empty())
		undoStack.pop();
}

void relese() {
	delete abcStock;//执行函数(厨师)

	delete buytenStockOrder;//方法对象(食物订单)
	delete buysixtyStockOrder;
	delete buyhundredStockOrder;
	delete selltenStockOrder;
	delete sellsixtyStockOrder;
	delete sellhundredStockOrder;

	delete broker;//方法管理者(服务生)
}

main.cpp

#include "redo.h"
/*只有在当前已经执行过命令的情况下redo命令才能生效,redo过的命令会进入
   undo栈,这样才能执行撤销恢复,一旦在redo和undo的过程中产生了新的命令
   入队,undo栈会立即被清空,在执行命令的子线程中每执行一次命令都会
   检查更新redo栈,确保陈旧的redo命令被消除。*/

int main() {
	int command = 0;

	thread t(_thread);
	while (1) {
		cout << printData(0) << endl;
		cout << "type your command: (" <<abcStock->showHeldnum()
			<<" shares held)"<< endl;
		cout << "1.buy(10)   2.buy(60)    3.buy(100) 4.sell(10)" << endl;
		cout << "5.sell(60)  6.sell(100)  7.redo     8.undo" << endl;
		cin >> command;
		switch (command) {
		case 1:broker->takeOrder(buytenStockOrder); broker->clearUndoStack(); Sleep(1500); system("cls"); break;
		case 2:broker->takeOrder(buysixtyStockOrder); broker->clearUndoStack(); Sleep(1500); system("cls"); break;
		case 3:broker->takeOrder(buyhundredStockOrder); broker->clearUndoStack(); Sleep(1500); system("cls"); break;
		case 4:broker->takeOrder(selltenStockOrder); broker->clearUndoStack(); Sleep(1500); system("cls"); break;
		case 5:broker->takeOrder(sellsixtyStockOrder); broker->clearUndoStack(); Sleep(1500); system("cls"); break;
		case 6:broker->takeOrder(sellhundredStockOrder); broker->clearUndoStack(); Sleep(1500); system("cls"); break;
		case 7:printData(broker->redoOrders()); Sleep(1500); system("cls"); break;
		case 8:printData(broker->undoOrders()); Sleep(1500); system("cls"); break;
		default: cout << "invalid command!" << endl; break;
		}
	}
	t.join();
	
	relese();

	system("pause");
	return 0;
}

你可能感兴趣的:(c++)