最新公告
  • 欢迎您光临源码库,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入
  • C++命令模式在事务性系统中的撤销重做机制

    C++命令模式在事务性系统中的撤销重做机制插图

    C++命令模式在事务性系统中的撤销重做机制:从理论到实战的完整指南

    作为一名长期从事金融系统开发的工程师,我深知事务性系统中撤销重做功能的重要性。记得在开发第一个交易系统时,我尝试了各种方法来实现撤销功能,最终发现命令模式是最优雅的解决方案。今天,我将分享如何利用C++命令模式构建强大的撤销重做机制,避免我当年踩过的坑。

    为什么选择命令模式?

    在事务性系统中,用户操作往往需要支持撤销和重做。传统的方法是将操作逻辑直接嵌入到业务代码中,但这会导致代码耦合度高、难以维护。命令模式通过将请求封装为对象,实现了操作与执行者的解耦。

    我曾在项目中尝试过直接记录状态快照的方式,但随着系统复杂度增加,内存占用急剧上升。命令模式只记录操作本身,大大减少了内存消耗,特别适合长时间运行的系统。

    基础架构设计

    让我们从最核心的接口开始。命令接口是所有具体命令的基类,定义了执行、撤销等基本操作:

    
    class Command {
    public:
        virtual ~Command() = default;
        virtual void execute() = 0;
        virtual void undo() = 0;
        virtual std::string getDescription() const = 0;
    };
    

    接下来是命令管理器,它负责维护命令的历史记录:

    
    class CommandManager {
    private:
        std::vector> undoStack;
        std::vector> redoStack;
        const size_t maxHistorySize = 100; // 防止内存无限增长
    
    public:
        void executeCommand(std::unique_ptr command) {
            command->execute();
            undoStack.push_back(std::move(command));
            
            // 清理重做栈
            redoStack.clear();
            
            // 限制历史记录大小
            if (undoStack.size() > maxHistorySize) {
                undoStack.erase(undoStack.begin());
            }
        }
        
        void undo() {
            if (undoStack.empty()) return;
            
            auto command = std::move(undoStack.back());
            undoStack.pop_back();
            command->undo();
            redoStack.push_back(std::move(command));
        }
        
        void redo() {
            if (redoStack.empty()) return;
            
            auto command = std::move(redoStack.back());
            redoStack.pop_back();
            command->execute();
            undoStack.push_back(std::move(command));
        }
        
        bool canUndo() const { return !undoStack.empty(); }
        bool canRedo() const { return !redoStack.empty(); }
    };
    

    实战:银行转账系统示例

    让我们通过一个银行转账的例子来具体实现。假设我们需要支持转账操作的撤销重做:

    
    class BankAccount {
    private:
        std::string accountNumber;
        double balance;
        
    public:
        BankAccount(const std::string& accNum, double initialBalance)
            : accountNumber(accNum), balance(initialBalance) {}
        
        void deposit(double amount) { balance += amount; }
        void withdraw(double amount) { balance -= amount; }
        double getBalance() const { return balance; }
        const std::string& getAccountNumber() const { return accountNumber; }
    };
    
    class TransferCommand : public Command {
    private:
        BankAccount& fromAccount;
        BankAccount& toAccount;
        double amount;
        bool executed{false};
        
    public:
        TransferCommand(BankAccount& from, BankAccount& to, double amt)
            : fromAccount(from), toAccount(to), amount(amt) {}
        
        void execute() override {
            if (!executed) {
                fromAccount.withdraw(amount);
                toAccount.deposit(amount);
                executed = true;
            }
        }
        
        void undo() override {
            if (executed) {
                toAccount.withdraw(amount);
                fromAccount.deposit(amount);
                executed = false;
            }
        }
        
        std::string getDescription() const override {
            return "Transfer $" + std::to_string(amount) + 
                   " from " + fromAccount.getAccountNumber() + 
                   " to " + toAccount.getAccountNumber();
        }
    };
    

    复合命令:处理复杂事务

    在实际系统中,一个业务操作可能包含多个子命令。比如银行的开户操作,需要创建账户、设置初始余额、记录日志等。这时可以使用复合命令:

    
    class CompositeCommand : public Command {
    private:
        std::vector> commands;
        std::string description;
        
    public:
        CompositeCommand(const std::string& desc) : description(desc) {}
        
        void addCommand(std::unique_ptr command) {
            commands.push_back(std::move(command));
        }
        
        void execute() override {
            for (auto& command : commands) {
                command->execute();
            }
        }
        
        void undo() override {
            // 注意:撤销顺序与执行顺序相反
            for (auto it = commands.rbegin(); it != commands.rend(); ++it) {
                (*it)->undo();
            }
        }
        
        std::string getDescription() const override {
            return description;
        }
    };
    

    性能优化与内存管理

    在实际项目中,我遇到过命令历史占用过多内存的问题。以下是几个优化技巧:

    
    // 宏命令:延迟加载命令数据
    class MacroCommand : public Command {
    private:
        std::function()> commandFactory;
        std::unique_ptr command;
        
    public:
        MacroCommand(std::function()> factory)
            : commandFactory(factory) {}
        
        void execute() override {
            if (!command) {
                command = commandFactory();
            }
            command->execute();
        }
        
        void undo() override {
            if (command) {
                command->undo();
            }
        }
        
        std::string getDescription() const override {
            return command ? command->getDescription() : "Macro Command";
        }
    };
    

    错误处理与事务一致性

    在事务性系统中,错误处理至关重要。我建议采用以下策略:

    
    class SafeCommand : public Command {
    private:
        std::unique_ptr command;
        
    public:
        SafeCommand(std::unique_ptr cmd) : command(std::move(cmd)) {}
        
        void execute() override {
            try {
                command->execute();
            } catch (const std::exception& e) {
                // 记录日志,确保系统状态一致
                std::cerr << "Command execution failed: " << e.what() << std::endl;
                throw; // 重新抛出,让调用者处理
            }
        }
        
        void undo() override {
            try {
                command->undo();
            } catch (const std::exception& e) {
                std::cerr << "Command undo failed: " << e.what() << std::endl;
                throw;
            }
        }
        
        std::string getDescription() const override {
            return command->getDescription();
        }
    };
    

    实际应用中的注意事项

    根据我的经验,在实现撤销重做机制时需要注意以下几点:

    1. 状态一致性:确保命令的execute()和undo()操作是严格互逆的。我曾经因为一个边界条件处理不当,导致撤销后状态不一致。

    2. 内存管理:设置合理的历史记录上限,避免内存泄漏。可以使用智能指针自动管理内存。

    3. 线程安全:在多线程环境中,需要对命令管理器进行适当的同步保护。

    4. 序列化支持:如果需要持久化命令历史,考虑添加序列化功能。

    总结

    命令模式为事务性系统提供了强大的撤销重做能力,通过将操作封装为对象,实现了良好的解耦和扩展性。在实际项目中,我建议从简单开始,逐步添加复合命令、错误处理等高级特性。

    记住,好的架构不是一蹴而就的。我在第一个版本中只实现了基本的撤销功能,随着需求变化逐步完善。希望这篇文章能帮助你在自己的项目中实现优雅的撤销重做机制,避免重蹈我当年的覆辙。

    1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
    2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
    3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
    4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
    5. 如有链接无法下载、失效或广告,请联系管理员处理!
    6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!

    源码库 » C++命令模式在事务性系统中的撤销重做机制