2012-03-19 68 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>的框架。