wxPython: Как я должен организовать данные для каждого виджета в контроллере?

У меня есть виджет, который отображает иерархию файловой системы для удобного просмотра (в основном, элемент управления деревом и некоторые связанные с ним кнопки панели инструментов, такие как «обновление»). Каждый из этих виджетов имеет набор базовых каталогов для отображения (рекурсивно). Предположим, что пользователь может создавать экземпляры таких виджетов, какие они находят удобными. Обратите внимание, что эти виджеты не соответствуют каким-либо бизнес-данным – они не зависят от модели.

Где должен (на виджет) набор базовых каталогов жить в хорошем дизайне MVC?

Когда кнопка обновления нажата, событие захватывается контроллером, а событие содержит соответствующий виджет файловой системы. Контроллер определяет базовые каталоги для этого конкретного виджета (каким-то образом), проходит этот путь к каталогу и передает виджет некоторым данным для рендеринга.

Два места, которые я могу запомнить для хранения базовых каталогов:

  1. Простое решение: сделайте базовые каталоги переменной экземпляра в виджетах и ​​попросите контроллер манипулировать им, чтобы сохранить состояние для этого виджета. Однако есть концептуальная проблема: поскольку виджет никогда не смотрит на эту переменную экземпляра, вы просто проецируете одну из обязанностей контроллера на виджет.
  2. Более (технически, возможно концептуально) комплексное решение: Храните сопоставление {widget: base_directory_set} в контроллере со слабыми ключевыми ссылками.

Второй способ позволяет легко расширять обязанности контроллера позже, поскольку имеет смысл делать вещи в контроллере – например, если я решил, что позже я решил определить набор всех базовых каталогов для всех этих виджетов.

Может быть, есть некоторые знания MVC, которые я пропускаю, что хорошо решает эту проблему.

Аномалия (с точки зрения MVC), которая делает эту конструкцию сложной для создания MVC-совместимого, заключается в том, что вы хотите отображать информацию, которая, по вашей концепции, «не живет в модели». В MVC нет такой вещи, как «информация, которая не живет в модели»: ее концептуальный корень – «модели содержат всю информацию, представления просто выполняют задачи презентации, контроллеры опосредуют взаимодействие с пользователем».

Вполне возможно, что отображаемая вами информация не «соответствует каким-либо бизнес-данным», но (в мировоззрении MVC) это не означает, что информация «не зависит от модели», потому что такой вещи нет – это просто означает, что вам нужен еще один класс модели (помимо того, что вы используете для хранения «бизнес-данных»), для хранения этих «не-деловых» данных! -)

Поэтому, когда пользователь «создает экземпляр виджета» (создает вид отображения каталога, предположительно, каким-то действием пользователя на каком-либо главном / координирующем представлении, возможно, на другом существующем виджете, если «клонирование» является одним из способов создания экземпляра виджета), контроллер отвечает за создание как объекта виджета, так и экземпляра «класса модели отображения каталога» и устанавливает соединение между ними (обычно, устанавливая в виджет ссылку на соответствующий экземпляр модели), а также рассказывая модели его первоначальная загрузка информации. Когда действие пользователя в виджетах подразумевает действие на модели, контроллер извлекает из виджета, участвующего в событии ссылку на экземпляр модели, и отправляет этот экземпляр соответствующим запросам (это бизнес модели, чтобы позволить представлению [s] заинтересованные в нем знают об изменениях в информации – как правило, с помощью некоторого шаблона наблюдателя, это определенно не бизнес контроллера для подачи представления информацией – это действительно очень отличный подход от MVC!).

Требуется ли в этом случае архитектурные инвестиции, требуемые MVC, по сравнению с более грубым подходом, когда информационные потоки менее древние, а модель, которая должна быть там, просто не существует? Я прагматик, и я определенно не поклоняюсь алтарю MVC, но я думаю, что в этом случае (относительно небольшие) инвестиции в звук, ясная архитектура действительно может погасить себя в изобилии. Речь идет о представлении вероятных направлений изменений – например, какая функциональность, которая вам не нужна прямо сейчас (но вполне может войти в картину вскоре после этого), будет тривиальной, если вы перейдете по надлежащему маршруту MVC, и быть кошмаром ad hoc kludges в противном случае (или потребовать несколько болезненного реорганизации всей архитектуры)? Всевозможные вероятные вещи, от желания отображать одну и ту же информацию о каталоге в разных виджетах, чтобы иметь более умную модель просмотра «информация о каталоге», которая может автоматически обновляться по мере необходимости (и предоставлять новую информацию непосредственно заинтересованным представлениям с помощью обычного шаблона наблюдателя , без участия контроллера), являются естественными и тривиально легкими с MVC (эй, это все дело MVC, в конце концов, так что это неудивительно!), kludgy и хрупкий с ad-hoc угловой архитектурой – небольшие инвестиции, большие потенциальные доходы, идите на это!

Вы можете заметить из тома предыдущего абзаца, что я не поклоняюсь алтарю «экстремального программирования» – как прагматик, я сделаю небольшой «дизайн впереди» (особенно с точки зрения введения чистого , гибкая, расширяемая архитектура, с самого начала, даже если это не незаменимо сейчас ) – именно потому, что, по моему опыту, немного предусмотрительные и очень скромные инвестиции, особенно на архитектурном фронте, многократно окупаются для себя во время (в таких разнообразных валютах, как масштабируемость, гибкость, расширяемость, ремонтопригодность, безопасность и т. д., хотя не все из них будут применяться ко всем проектам – например, в вашем случае безопасность и масштабируемость на самом деле не являются проблемой. но другие аспекты, скорее всего, будут! -).

Только для общности позвольте мне отметить, что это прагматичное отношение мое не оправдывает чрезмерную энергию и время, затрачиваемое на сбор архитектуры (по определению слова «чрезмерное» 😉 – знакомство с несколькими фундаментальными архитектурными узорами (и MVC, безусловно, является одним из них) часто сокращает первоначальные инвестиции с точки зрения времени и усилий – как только вы осознаете, что такая классическая архитектура вам хорошо послужит, так как в этом случае очень просто понять, как ее воплотить (например, отвергают идею «MVC без M»!), и на самом деле это не делает гораздо больше кода по сравнению с самыми сложными, быстрыми горячими клавишами! -)

Основываясь на том, как работает методология 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) 

Надеюсь, это по крайней мере даст вам кое-что о чем подумать.

Решение, которое я сейчас принял, – «контроллеры для каждого виджета». (Возможно, для этого существует существующее имя.) Он делегирует «родительский» контроллер для любых пользовательских интерфейсов, но существует для управления виджетами и связывания любых соответствующих данных на основе каждого виджета.

Концепция «контроллер виджета» позволяет избежать проецирования каких-либо нерелевантных свойств на сам виджет. Вы можете расширить эти контроллеры для регистрации / отмены регистрации контролируемых виджетов при создании / уничтожении в те моменты, когда вы хотите выполнить операцию с большим количеством виджета, тем самым избегая магии 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 будет уничтожен, контроллер и связанные с ним данные, естественно, исчезнут вместе с ним.