понедельник, 7 февраля 2011 г.

Графический интерфейс, часть вторая

Продолжение. Начало смотрите в первой части.

Отлично!

  • Model-View-Controller -- фикция.
  • Приложения должны использовать Model-Widget.
  • Всё хорошо?

Начнем с того, что модель с точки зрения приложения - это совсем не то же самое что модель для виджета.

Виджет:

  • Зачастую модели не имеет. Какая модель у поля ввода? А у метки (label), содержащей лишь статический текст? Такие виджеты, как правило, не утруждают себя созданием примитивной модели. Вместо этого у виджета имеются методы вроде set_text и get_text, их достаточно.

  • Модель появляется у сложных виджетов. Например, QTreeView, - дерево с несколькими колонками, - имеет модель типа QAbstractItemModel.

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

  • Модели виджетов появились в ответ на появление в библиотеках GUI комплексных виджетов. Это просто следствие декомпозиции.

    Если экранный элемент имеет нетривиальное логическое представление и сложную схему взаимодействия с пользователем - естественным образом возникает желание разделить его на несколько классов.

    Как минимум два класса, - сам виджет и адаптер для представляемых им данных, - становятся доступны разработчику.

    Таких классов может быть и больше - довольно популярным является вынесение валидаторов и декораторов, отвечающих за внешний вид элементов сложного виджета. Тот же QTreeView имеет itemDelegate, itemDelegateForColumn и itemDelegateForRow, позволяющие управлять внешним видом и встроенными редакторами ячеек.

    А также selectionModel, контролирующую правила выделения отдельных вложенных элементов.

  • В последующем изложении я буду использовать термин адаптер для таких моделей.

Пользовательское приложение (application):

  • Модель для приложения выполняет совершенно другую роль. Это - представление предметной области.

  • Модель приложения отвечает за операции с пользовательскими данными и бизнес-логику.

  • Как правило, имеет смысл по меньшей мере на уровне формы - виджеты отображают лишь часть модели, в то время как сама модель представляет собой цельную и законченную абстракцию из предметной области.

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

Лишь по исторически сложившимся причинам адаптер часто называют моделью. Такое именование имеет смысл при разработке библиотеки графического интерфейса, но абсолютно неоправданно при создании конечного продукта.

Отказываться от термина модель очень трудно, этот концепт имеет давнюю устоявшуюся традицию. Поэтому будем разделять модель и адаптер.

Проблема сообщений графического интерфейса.

Любая библиотека GUI имеет встроенную систему сообщений.

Изначально сообщения создавались для обработки пользовательского ввода: нажатий клавиш, манипуляций мышью.

Естественным образом добавились другие события: изменение размеров окна-формы, запрос на перерисовку, закрытие окна и т.д.

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

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

Разделение зависит от конкретного инструмента, но присутствует наблюдающаяся закономерность: сообщения операционной системы обычно транслируются в сообщения библиотеки.

При разработке внутренней логики создатели для каждого конкрентого виджета делают выбор заново. Играет роль и цена накладных расходов на пересылку сообщений, и удобство программирования (вызвать метод всегда легче), и просто вкусовые предпочтения.

Снова перейдем к прикладному программированию.

Имеет ли смысл пользоваться сообщениями во время проектирования бизнес-логики?

Очевидно, да: с точки зрения объектно ориентированного подхода даже вызов метода является посылкой сообщения. Существуют и более сложные способы: асинхронные сообщения, обратные вызовы (callbacks).

Стоит ли использовать сообщения библиотеки GUI?

Ответ неоднозначный.

Естественно, взаимодействие с пользователем обязано базироваться за сообщениях библиотеки GUI - просто потому, что она так работает.

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

Это примерно как мультипоточность: можно использовать низкоуровневые средства синхронизации потоков операционной системы и выдавать в результате хорошо работающий код.

Другой вопрос, что при переходе на другую операционную систему (иную библиотеку GUI в нашем случае) потребуется кардинальное переписывание программы.

Есть и более сложная, концептуальная проблема. Если предметная область вообще и бизнес-логика в частности инвариантна к библиотеке графического интерфейса, то почему реализация этой области становится крепко завязана на низкоуровневый механизм передачи сообщений?

Другими словами, существуют ли общие концепции, подходы и шаблоны проектирования моделей - не завязанные на механизм передачи сообщений лежащего ниже слоя GUI?

Заключение

"Модель" есть представление предметной области. Используемые библиотеками графического интерфейса модели на самом деле к предметной области отношения не имеют. Будем называть их адаптерами к "правильным" моделям, позволяющим виджетам взаимодействовать с объектами предметной области.

Отступление: не люблю слово "сущность", слишком часто применяемое не к месту. Если автор постоянно говорит о "сущностях", перескакивая при этом с одного на другое - утомляет. "Сущность" - слишком абстрактное понятие, не стоит маскировать им недостаточный уровень владения литературным языком.

Резюмируя:

  • Есть предметная область и ее бизнес-правила.

  • Предметную область удобно описывать в терминах "моделей". Это очень естественное определение.

  • "Модели" библиотек графического интерфейса (GUI) означают нечто совсем отличное от того, то подразумевают модели предметной области. Назовем модели GUI адаптерами, чтобы уходя от неоднозначности терминов.

  • Модели и GUI взаимодействут через механизм сообщений GUI. При этом внутренняя логика модели не обязана использовать эти сообщения - требуется иная, более подходящая для работы с предметной областью схема:

    1. GUI передает модели информацию о ее изменении, вызванной действиями пользователя.

    2. Модель проводит измение своего состояния, реагируя на ввод.

      Как она это делает - забота одной лишь модели.

      Главное - все действия должны быть выполнены на этом шаге. Нарушение правила ведет к потенциальной бесконечной рекурсии.

      Длительные по времени изменения разделяются на конечные этапы, демонстрирующие progress bar или другую сигнализацию активности.

    3. GUI получает извещение о новом состоянии модели, отображая их в пользовательском интерфейсе.

  • Распространенные библиотеки GUI потребовали огромное количество человеко-часов на их создание. Они широко используются и обладают массой очевидных достоинcтв. Средства автоматического генерирования интерфейса пользователя - далеко не последний момент, особенно для неопытных программистов.

  • Хороший подход должен при минимальных изменениях обеспечивать склейку любого UI с моделью.

Проблемы (далеко не все) - показаны. Пора переходить к решениям.

Продолжение - в третьей части.

2 комментария:

  1. Очень классно описал проблему привязки и зависимости бизнес-логики и данных к GUI.

    Ко всему вышесказанному только один вопрос: "Почему ты забил на Ocean"? =)
    В контексте вышесказанного из него получается конфетка...

    ОтветитьУдалить
  2. Ocean был занятной поделкой, не более. Невозможно на равных соревноваться с распространенными библиотеками GUI.

    Кроме того, хорошая система должна цепляться к любому GUI с одинаковым успехом, будь это Qt, wxWidgets, Win32 API или ncurses.

    ОтветитьУдалить