为C++类设计,以撤消对另一个类成员的修改

Design for a C++ class to undo modifications to another class member

本文关键字:另一个 成员 修改 撤消 C++      更新时间:2023-10-16

我有一个设计繁琐的对象组合。类别X和Y是该设计的示意图,其中Y是X.的一个组件

class Y {
public:
    std::string _name;
    Y(std::string name) : _name(name) {}
};
class X {
    Y _y;
public:
    X(std::string name) : _y(name) {}
    Y getY() { return _y; }
    Y* getYPtr() { return &_y; }
};

注意,Y中的std::string _name是公共的。

我想做的是通过X的实例访问Y::_name,为其写入新值,并有可能在程序的其他部分轻松撤消写入操作。

我的尝试如下:我使用一个Undo对象,它包含三个信息:

  • 指向撤消生效的字符串的指针
  • 包含旧名称的字符串
  • 包含新名称的字符串

class Undo {
    std::string _oldName;
    std::string _newName;
    std::string *_internalName;
public:
    Undo(std::string *name) : _internalName(name) {}
    void setOldName(std::string oldName) {
        _oldName = oldName;
    }
    void setNewName(std::string newName) { 
        _newName = newName;
    }
    void undoToOldName() {
        *_internalName = _oldName;
    }
};

如果我想撤消写操作,我只需要调用Undo对象上的undoToOldName()方法。

示例:

X x("firstName");
Y *y = x.getYPtr();
// Prepare the undo object
Undo undo(&(y->_name));
undo.setOldName(y->_name);
undo.setNewName("secondName");
// Set new name
y->_name = "secondName";
// Output: secondName
std::cout << x.getY()._name << std::endl;
// Undo
undo.undoToOldName();
// Output: firstName
std::cout << x.getY()._name << std::endl;

我不喜欢这种设计的一点是需要Y * getter。

作为约束,我不能改变作文的设计。

你能为这个提出替代设计吗?

谢谢。

使用对象修饰符保护修订的不变性异常棘手。如果您需要同时回滚多个字段,而其中一个字段失败,您该怎么办?您的修订控件实际上也可能影响对象的正常操作。

对对象进行版本化的一个简单方法是保留对象的所有版本。当您需要回滚时,只需将其替换为旧版本的副本。

一些注释:Undo对象不应该需要setOldName方法。它可以计算出来,因为它有字符串指针。其次,它也不需要setNewName;它只需要一个方法来告诉它何时设置新值。(假设你需要它,我对此表示怀疑)

一个不错的设置是让getYPtr()返回一个undo_ptr<Y>。这是一个薄垫片,它知道相关的Undo对象。当调用undo_ptr<Y>::~undo_ptr时,即当客户端完成时,调用关联的Undo::newNameSet方法。如上所述,这只是通过提供的指针提取新值。

示例:

X x("firstName");
{  
  Undo undo(x, &X::name); // Slightly cleaner interface. Saves "firstName".
  Y* y = x.getYPtr();
  y->_name = "secondName";
  // Output: secondName
  std::cout << x.getY()._name << std::endl;
  // Undo (calls Undo::operator(), the convention for functors).
  undo();
  // Output: firstName
  std::cout << x.getY()._name << std::endl;
}

正如您所看到的,在这种情况下不需要捕获新名称,因此您不需要undo_ptr<Y>的框架。

为什么要创建一个新类?您可以使用beginTransactioncommitTransactionrollbackTransaction方法扩展类X,以及一个update方法,如果在事务外调用该方法,该方法将可选地断言。

相关文章: