2012-03-19 36 views
2

我有一个笨重设计的对象组合。 类X和Y是这样的设计,其中Y是X的分量为C++类设计撤消对其他类成员的修改

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; } 
}; 

注意std::string _nameY是公共的示意性表示。

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

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

  • 的字符串指针在其上撤消生效
  • 含有使用oldName
  • 的字符串的字符串包含新名称

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; 
    } 
}; 

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

例子:

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 *吸气。

作为一个约束,我无法改变作曲的设计。

您能否为此建议其他设计?

谢谢。

+0

是否允许使用'X :: setY()'方法?如果是这样,它将允许移除'getYPtr()'和实现'X :: rollbackY()'方法。 – hmjd 2012-03-19 11:12:41

+0

也许让你的Undo类成为Y的朋友,所以它可以设置'_y._name'?如果你想撤销记住哪个X或Y工作,它可以在构造函数中引用并使用初始化列表记录它以备后用。避免指针。坦率地说,尽管......:( – 2012-03-19 11:16:25

+0

@hmjd谢谢你的评论,不过,这是不可能的,因为一个'X'有许多类似于'Y'的对象,其中只有一个是有效的,这是由一个Enumerate决定的,一旦我发现要编辑哪个'Y',我不想再遍历Enumerates了,另外,'Y'有很多'_y's,并不是所有的都是有资格申请撤消,我承认设计很丑陋,但那是我拥有的图书馆:)。 – 2012-03-19 11:26:12

回答

1

保护对象修饰符修订的不变性特别棘手。如果你需要一次回滚多个字段而一个失败,你会怎么做?您的版本控制实际上可以影响对象的正常运行方式。

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

0

为什么一个新班?您可以使用beginTransaction,commitTransactionrollbackTransaction方法以及update方法扩展X类,如果在事务之外调用该方法,该方法可以选择性地进行声明。

1

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

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

例子:

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>的框架。