2011-04-17 60 views
0

我有一個C++應用程序,根據經典Model-View-Controller pattern設計。該模型通過控制器接口由外部源通過Command pattern進行修改。這些命令由一個Action對象(及其派生物)表示。在MVC設計中撤銷功能

現在我想能夠撤消修改,但我的問題是我沒有在我的控制器中的getters,只有setter。這似乎很合乎邏輯,因爲沒有理由有人應該能夠通過控制器獲得關於模型的信息。因此,我無法讓Action對象存儲模型的狀態,因爲他們無法訪問它。

如何解決這個問題?我想保留我的應用程序儘可能擴展,我不太確定哪個選項最適合。我到目前爲止的方法是:

  1. 將getter方法放在控制器中。這似乎違背了MVC模式。
  2. 給動作一個指向視圖的指針。然後,該操作可以:
    1. 使用單個獲取器來獲取要修改的模型的特定元素的狀態。
    2. 使用由查看器實現的Memento方法。

也許還有更好的方法來做到這一點?現在,成爲最好的選擇似乎是2,子選項1(子選項2,我可能存儲更多的狀態比撤消一個動作所需的更多)。

注:我知道還有關於如何實現撤消操作的其他問題。然而,我發現的唯一答案給了使用Command或Memento模式的建議。我知道這是最有可能的路要走。我所要求的是如何在MVC設計中儘可能地將其擴展爲可擴展的。

[編輯]我不喜歡的紀念圖案是它迫使我存儲一個完整的狀態。假設我的模型是一個1000x1000矩陣,我的命令是ChangeOneValueAtLocation。爲了能夠撤銷它的更改,ChangeOneValueAtLocation對象只需要存儲它正在改變的位置的先前值,但對於Memento來說這似乎不可能。我的模型越大,這個問題就會變得最大。

[編輯2]另一個問題我在本申請的特定情況中具有紀念品:每一個命令對象可以在模型執行方法,有,做完全相反(或可以很容易地被誘導的方法這樣做)。這就是爲什麼我會覺得必須存儲整個狀態是一件很浪費的事情,應該不需要這樣做,恢復單個命令非常簡單,唯一的問題是讓數據能夠做到這一點。

此外,我不需要能夠撤消一個特定的命令,只有在我的歷史堆棧上的最頂層。

回答

2

我也支持撤消工作包含撤消支持的模型圖層。模型方面有很多方法可以處理這個問題。第一個也是最明顯的是模型自己用「標籤」記住變化的歷史,但是這可能很難爲所有模型類同步。

另一個選擇是創建一個具有「事務」概念的歷史記錄管理器,該歷史記錄管理器可以生成一個撤消點並拍攝模型的快照,或者開始記錄更改(以減少內存使用量) ,或者記錄導致模型變化的命令等等。模型通知管理者變化,最後你完成交易(或者不交,因爲下一次交易的開始可以是前一交易的結束)。一旦你添加了回滾到某個點的能力,工作就完成了。通過在這個管理器類中使事情稍微複雜一點,您可以創建一個撤消樹(就像emacs中的那個),所以它也是一種相當靈活的方法。

雖然上面的解決方案並不在模型層中。它是由模型和控制器驅動的支持類。如果刪除事務概念,那麼它完全是模型驅動的,但實現撤銷操作的概念可能有點棘手。如果將其更改爲命令代理,則它是控制器使用的唯一實體,顯然是一種模型。在這一點上,選擇一種方法比另一種方法設計太粗糙,但我傾向於「交易」模式。實施起來感覺很簡單。

+0

我決定採用這種方法,因爲它可以讓我輕鬆地將模型及其相關歷史記錄傳輸到異地客戶端。將歷史記錄放入控制器會使這變得更加困難。此外,我的模型只有一個前端類,所以我不認爲非原子撤銷問題「sehe」提及會成爲問題。 – Darhuuk 2011-04-20 12:11:21

0

在模型本身中構建撤消功能。讓你模型保存一個命令列表。視圖向模型傳遞撤消信號時,按相反順序運行命令。

2

我真的建議建立撤銷樹到控制器中

構建成的模型可以運行你陷入困境:

  • 的「模式」通常是按次分段(每個視圖有它自己的局部模型)
  • 這將導致非原子撤消(撤消的操作部分由於視圖不知道什麼其他的事情(型號)將必須被撤銷等)

控制器是「行動調度員」,所以不得不說

  1. 克隆狀態(所有型號)快照
  2. 添加操作參考歷史快照
  3. 運行動作

然後撤消將

  1. 彈出行動關閉histor Ÿ堆棧(可選推到「未來」棧)
  2. 恢復快照
  3. 顯示視圖

此外,還要有高水平的動作(見組合模式或命令模式)

+0

但現在的問題仍然是:我該如何幹淨地實現它?我的Command類如何獲得它的快照?從實現一個Memento模式的View中,需要一個指向View的指針?從控制器中的一個獲取器開始運行,從而打破了MVC模式? – Darhuuk 2011-04-17 21:01:22

+0

控制器_is_負責狀態。所以,讓控制器記住狀態,不會打破MVC模式。但是,不應該將其暴露出來。你可以做Controller.Rollback() - 控制器可以選擇將實際實現委託給Model(如果它實現了Memento,例如) – sehe 2011-04-17 21:04:08

+1

事情是我想讓狀態進入我的Command對象。因此''Controller.rollback()''可以從堆棧中彈出最後一個命令並調用''undo()''。我想我可以讓Controller在調用''execute()''之前將狀態放入Command中。我不喜歡保存完整狀態的想法(請參閱我原始問題中的編輯以獲取更多信息)。 – Darhuuk 2011-04-17 21:37:28