2009-11-06 23 views
4

我有一個小部件,顯示一個文件系統層次結構,方便瀏覽(基本上是一個樹形控件和一些相關的工具欄按鈕,如「刷新」)。每個小部件都有一組基本目錄供它顯示(遞歸)。假定用戶可以實例化這些小部件中的許多小部件,因爲他們發現方便。請注意,這些小部件不對應於任何業務數據 - 它們獨立於模型。wxPython:我應該如何組織控制器中的每個控件的數據?

基本目錄集(每個小部件)應該在哪裏生活在良好的MVC設計中?

當按下刷新按鈕時,事件被控制器捕獲,並且事件包含相應的文件系統瀏覽器小部件。控制器(以某種方式)確定該特定小部件的基本目錄,遍歷該目錄路徑,並傳遞該小部件的一些數據以進行呈現。

兩個地方,我能想到的存儲基礎目錄:

  1. 最簡單的辦法:讓基目錄上的小部件的實例變量,並有控制器操縱它保留狀態,該部件。但是,這存在一個概念問題:由於小部件從不查看該實例變量,因此您只是將控制器的責任之一投射到小部件上。
  2. 複雜的解決方案越多(在技術上,可能是概念上):在控制器中使用弱關鍵引用保留{widget: base_directory_set}映射。

第二種方式可以很容易地擴展的控制器職責以後,如把東西控制器傾向於做的事情 - 例如,如果我決定以後要確定該組所有所有基本目錄這些小部件。

可能有一些我錯過的MVC知識,很好地解決了這種問題。

回答

2

的異常(從MVC的觀點),使得這樣的設計很難使MVC符合的是要顯示的信息,通過你的概念,「不生活在一個典範」。 MVC中沒有「模型中沒有的信息」這樣的東西:概念根是「模型保存所有信息,視圖只是做演示任務,控制器調解用戶交互」。

這很可能是你顯示的信息不「對應於任何業務數據」,但(在MVC的世界觀)這樣做意味着信息是「獨立於模型」,因爲有是不是這樣的事 - 它只是意味着你需要另一個模型類(無論你用什麼來保存「業務數據」),以保存這個「非業務」數據! - )

因此,當用戶「實例化一個窗口小部件」(創建一個目錄顯示視圖,大概是通過某些主/協調視圖上的用戶動作,如果「克隆」是實例化窗口小部件的方式之一,可能在另一個現有窗口小部件上),控制器負責創造機器人ha小部件對象和「目錄顯示模型類」的實例,並建立它們之間的連接(通常通過在小部件上設置對相關模型實例的引用),並告訴模型執行其初始信息加載。當小部件上的用戶操作意味着對模型的操作時,控制器從涉及事件的小部件中檢索對模型實例的引用,並向該實例發送適當的請求(這是模型的業務,讓視圖[S]感興趣瞭解更改信息 - 通常由一些觀察者模式;它絕對不是控制器的業務信息養活的觀點 - 這是真正從MVC一個非常不同的方法!)。

是通過MVC值得的需要,你的情況的建築投資,相比粗糙的方法,把信息流不太純淨,並且應該在那裏並不存在的模式?我是一位實用主義者,我絕對不會在MVC的祭壇上敬拜,但我認爲在這種情況下,對健全,清晰的建築進行(相對較小的)投資確實可以大量償還。這是一個設想可能的變化方向的問題 - 例如,如果您使用正確的MVC路由,那麼您現在不需要的功能(但很快就會很快輸入畫面)將會變得微不足道,並且會否則就會成爲特別的問題的噩夢(或者需要對整個架構進行一些有點痛苦的重構)?所有這些可能的事情,從想要在不同的小部件中顯示相同的目錄信息到具有更智能的「目錄信息觀察」模型,其可以在需要時自動刷新自己(並且通過通常的觀察者模式直接向感興趣的視圖提供新的信息,沒有控制器的參與),MVC是自然而且容易的(嘿,這完全是MVC的全部,所以這並不令人驚訝!),kludgy和脆弱的特殊角落 - 切割建築 - 投資小,潛在回報大,爲之奮鬥!

你可以從上一段,我沒有在「極限編程」神壇拜的基調注意到無論是 - 作爲一個實用主義者,我做來講有點「設計了前」(尤其是從一開始,即使它不是必不可少的),這正是因爲根據我的經驗,一點點的深謀遠慮和非常適中的投資,特別是在建築方面,支付在項目生命週期內(在可擴展性,靈活性,可擴展性,可維護性,安全性等各種貨幣方面,多次重複使用),但並非所有項目都適用於每個項目 - 例如,在您的情況下,安全性和可擴展性並不是真正的問題......但另一個spects將可能! - )。

只是爲了一般性,讓我指出,我這個務實的態度確實證明過度精力和花費由單詞「過度」 ;-)的定義採摘的架構(時間 - 熟悉有一些基本的架構模式(MVC肯定是其中之一)通常會減少時間和精力方面的初始投資 - 一旦您認識到這樣的經典架構將爲您提供良好的服務,就像在這種情況下一樣,這很容易看看如何體現它(例如,拒絕一個「沒有M的MVC」的想法 - ),並且它並沒有真正花費更多的代碼,相比於最糟糕的特別快捷鍵!)

+0

這是非常有意義的,並且表明我對MVC理論的不完全理解 - 對於「可觀察」(或在小部件之間共享)的數據,無論「業務」如何,MVC模式更有用。即使我所瞄準的系統沒有類似inotify的能力(使刷新命令成爲必要),我可以看到重新定位這個小部件的平臺,通過觀察獲益。我認爲我現在對MVC的實用性在現在的節奏上更加了解 - 謝謝Alex! – cdleary 2009-11-16 04:19:52

+0

@cdleary,不客氣!是的,如果你不在Windows/Mac OS X/Linux/BSD區域,刷新確實是必要的 - 但一個好的MVC基金會可以讓你處理各種平臺,包括帶有和不帶有能力。是的,實用性確實打破了純粹性,但是MVC作爲基礎架構可能非常實用(它不是唯一的,但它爲各種有用的功能提供了良好的服務......並且,實現起來並不難)! - )。 – 2009-11-16 05:54:49

1

基於怎樣的MVC的方法進行操作,我建議你去修改你列出的第一個解決方案:

使基目錄上的小部件的實例變量,並有控制器操縱它到保留該小部件的狀態。

爲什麼?你說這些小部件是獨立於模型的,但它們是從模型中實際引用的嗎?如果你沒有將你的小部件綁定到你的模型上,你會偏離MVC的基本概念。

我沒有任何有關wxPython的知識,所以我不能說它如何符合MVC,如果有的話。即使如此,我認爲你應該考慮將小部件集成到模型中,或者將它們自己當作模型來對待。

因此,如果我們假設在這種情況下,小部件實際上是模型層次結構的一部分,這可能不僅僅是簡單的解決方案,正如您所說的那樣,而是正確的解決方案。

因爲MVC的核心基礎之一是保持每個部分之間的鬆散耦合,所以您總是希望將業務邏輯與數據輸入和表示隔離。維護與模型分開顯示的小部件會破壞這一點,因此將任何類型的信息方法都放入控制器中並不適用。您希望模型包含控制器在視圖中呈現數據時需要操縱或顯示的所有內容。

你有沒有考慮爲所有部件創建一個超類,這樣會有一套通用的,他們將始終以繼承的方法呢?

例子:

import os 

WIDGET_PREFIX = '/tmp' 
class Widget: 
    def __init__(self, name): 
     self.name = name 
     self.widget_prefix = WIDGET_PREFIX 
     self.dirs = os.walk(os.path.join(self.widget_prefix, name)) 

    def _get_base_directory_set(self): 
     return self.dirs 
    base_directory_set = property(_get_base_directory_set) 

我希望這至少給你一些思考。

+0

我掛起是顯示的目錄不是「商業數據」(即它不會被序列化或存儲在任何地方),它只是關於UI狀態的元數據。 wxPython只是一個窗口系統的庫,所以MVC分離是我自己的一個可維護系統的努力。 – cdleary 2009-11-15 04:20:08

+0

那麼每個小部件目錄本身存儲在文件系統中,對嗎?因此,即使不在數據庫列中,它仍然是綁定到小部件的數據,因此也是其中的一部分。我仍然建議你修改你的小部件,以擁有或繼承一種方法來自動顯示這些信息,而不是靜態的。 – jathanism 2009-11-15 19:55:51

0

目前我採用的解決辦法是,「每個小部件控制器。」 (也許有一個現有的名字。)它爲任何UI範圍的功能委派一個「父」控制器,但存在來控制該小部件並在每個小部件的基礎上關聯任何相關數據。

在「各插件控制器」的概念避免了突出任何不相關的屬性到插件本身。您可以擴展這些控制器以在創建/銷燬時註冊/取消註冊受控小部件,以便執行多小部件操作的時候,從而避免weakref魔術。

例如:

class FSBrowserController(wx.EvtHandler): 

    """Widget-specific controller for filesystem browsers. 

    :ivar parent: Parent controller -- useful when UI-wide control is 
     necessary to respond to an event. 
    """ 

    def __init__(self, parent, frame, tree_ctrl, base_dirs): 
     self.parent = parent 
     self.frame = frame 
     self.tree_ctrl = tree_ctrl 
     self.base_dirs = base_dirs 
     frame.Bind(EVT_FS_REFRESH, self.refresh) 
     frame.Bind(wx.EVT_WINDOW_DESTROY, self._unregister) 
     self.refresh() 
     self._register() 

    def _register(self): 
     """Register self with parent controller.""" 
     self.parent._fsb_controllers.append(self) 

    def _unregister(self, event): 
     """Unregister self with parent controller.""" 
     if event.GetEventObject() == self.frame: 
      self.parent._fsb_controllers.remove(self) 

    def refresh(self, event=None): 
     """Refresh the :ivar:`tree_ctrl` using :ivar:`base_dirs`.""" 
     raise NotImplementedError 


class Controller(wx.EvtHandler): 

    """Main controller for the application. 
    Handles UI-wide behaviors. 
    """ 

    def __init__(self): 
     self._fsb_controllers = [] 
     fsb_frame = FSBrowserFrame(parent=None) 
     FSBrowserController(self, fsb_frame, fsb_frame.tree_ctrl, 
      initial_base_dirs) 
     fsb_frame.Show() 

這種方式,當FSBrowserFrame被破壞,控制器和相關聯的數據,自然會與它消失。

相關問題