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

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

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

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

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

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

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

Возможно, мне не хватает каких-то знаний о MVC, которые хорошо решают проблемы такого рода.


person cdleary    schedule 06.11.2009    source источник


Ответы (3)


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

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

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

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

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

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

person Alex Martelli    schedule 15.11.2009
comment
Это имеет смысл и демонстрирует мое несовершенное понимание теории MVC — для данных, которые можно наблюдать (или совместно использовать виджеты), независимо от того, как это работает, шаблон MVC более полезен. Несмотря на то, что системы, на которые я ориентируюсь, не имеют возможностей, подобных inotify (что делает необходимой команду обновления), я мог бы перенацелить этот виджет на платформы, которые действительно выиграли от наблюдения. Я думаю, что теперь я гораздо лучше понимаю, где практичность MVC превосходит чистоту — спасибо, Алекс! - person cdleary; 16.11.2009
comment
@cdleary, добро пожаловать! Да, если вы находитесь за пределами зоны Windows/Mac OS X/Linux/BSD, обновление действительно необходимо, но хорошая основа MVC позволит вам работать с различными платформами, в том числе с такими платформами и без них. возможности. Да, практичность превосходит чистоту, но MVC как базовая архитектура может быть довольно практичной (она не единственная, но она хорошо справляется со множеством полезных функций... и это не так). это тоже сложно реализовать!-). - person Alex Martelli; 16.11.2009

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

Я надеюсь, что это, по крайней мере, даст вам пищу для размышлений.

person jathanism    schedule 13.11.2009
comment
Моя проблема заключается в том, что отображаемые каталоги не являются бизнес-данными (т. е. они нигде не сериализуются и не хранятся), это просто метаданные о состоянии пользовательского интерфейса. wxPython — это просто библиотека для оконных систем, поэтому разделение MVC — это все мои собственные усилия по созданию системы, которую можно обслуживать. - person cdleary; 15.11.2009
comment
Ну, сами каталоги для виджетов хранятся в файловой системе, верно? Таким образом, даже если они не в столбце базы данных, это все равно данные, привязанные к виджету и, следовательно, являющиеся его частью. Я по-прежнему рекомендую вам модифицировать ваши виджеты, чтобы они обладали или наследовали метод для автоматического, а не статического отображения этой информации. - person jathanism; 15.11.2009

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

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

Например:

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 контроллер и связанные с ним данные естественным образом исчезнут вместе с ним.

person cdleary    schedule 15.11.2009