Шаблон дизайна иерархии интерфейса? - PullRequest
0 голосов
/ 17 ноября 2009

Я на начальной стадии разработки многоплатформенного (мобильного) приложения на C ++ с графическим пользовательским интерфейсом. Я пытаюсь найти хороший способ абстрагирования фактической реализации интерфейса / окон. Вот в основном то, что я пробовал до сих пор:

Я создал интерфейсную иерархию

Screen
 +Canvas
 +Form
   +Dialog
Control
 +EditBox
 +CheckBox
 +...

и у меня также есть класс Application, который в основном использует все эти интерфейсы для реализации логики пользовательского интерфейса. Класс Application также предоставляет абстрактные фабричные методы, которые он использует для создания экземпляров класса UI.

Все эти интерфейсы имеют реализации. Например, для Windows Mobile Win32Form реализует Form, Win32Canvas реализует Canvas, а Win32Dialog реализует Dialog. Как видите, проблема в том, что реализации теряют иерархию, то есть Win32Dialog не расширяет Win32Form.

Это становится проблемой, например, в методе Form :: addControl (Control & ctrl). Я знаю, что в версии приложения для Windows Mobile параметр ctrl является одной из реализаций элемента управления Win32 .... Но поскольку иерархия потеряна, нет никакого способа узнать, является ли это Win32EditBox или Win32CheckBox, что мне действительно нужно знать для выполнения каких-либо специфических для платформы операций над элементом управления.

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

Пожалуйста, не говорите мне использовать ту или иную библиотеку пользовательского интерфейса; Я люблю кодировать вещи с нуля, и я в этом для обучения ...; -)

Ответы [ 6 ]

2 голосов
/ 17 ноября 2009

Вдохновение с моей стороны:

Не наследуйте форму от экрана. Это не реальность.

 Screen
 Canvas
 GUIObject
    Form
       Dialog
    Control
       Button
       EditBox
       CheckBox
       ...
       Panel

Более подробное описание:

Экран

  • "has-a" холст
  • обеспечивает отображение определенных функций
  • не поддавайтесь искушению наследовать от Canvas

Canvas

  • ваша абстрактная среда рисования
  • цель - холст экрана, холст формы, изображение, память ...

GUIObject

  • Wnd в некоторых иерархиях
  • обычно предоставляет некоторые возможности для самоанализа
  • обычно предоставляет интерфейс обработки сообщений

Форма

  • в некоторых наборах инструментов фактически называется "диалог"
  • "has-a" холст, на котором можно рисовать
  • Список элементов управления has-a или панель клиента. Панель управления
  • заголовок "has-a", полосы прокрутки и т. Д.

Диалог

  • Мне нравится называть диалоги стандартизированными YesNo, OpenFile и т. Д., В основном упрощенная форма (без кнопки закрытия, без меню и т. Д.)
  • может или не может быть унаследовано от формы

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

Это должно работать. Однако, как использовать специфические особенности каждой платформы, не нарушая универсальность этого интерфейса и / или простоту использования, является сложной частью. Я обычно стараюсь отделить все рисунки от этих базовых классов, и у меня есть какой-то специфичный для платформы «Painter», который может взять каждый базовый объект и нарисовать его в зависимости от платформы. Другим решением является параллельная иерархия для каждой платформы (я стараюсь избегать этой, но иногда требуется - например, если вам нужно обернуть дескрипторы окон) или подход «биты возможностей».

1 голос
/ 17 ноября 2009

Шаблон GOF, который решает эту проблему, является шаблоном моста. Ответ Konamiman объясняет это, вы используете делегирование / сдерживание, чтобы абстрагироваться от одной из проблем.

1 голос
/ 17 ноября 2009

Вы можете использовать сдерживание. Например, класс, который реализует Dialog, будет содержать Win32Dialog в качестве свойства, а класс, который реализует Form, будет содержать Win32Form; все доступы к членам Dialog или Form делегируют доступы к содержащимся классам. Таким образом, вы все равно можете Dialog наследовать от Form, если это имеет смысл для вашего проекта.

Сказав это, я вижу нечто странное в вашем дизайне интерфейса. Если я правильно понял (возможно, нет, то, пожалуйста, исправьте меня), Dialog наследуется от Form, а Form наследуется от Screen. Задайте себе вопрос, чтобы подтвердить этот дизайн: Является ли диалог также формой? Это тоже Screen? Наследование следует использовать только тогда, когда имеет смысл is-a .

0 голосов
/ 17 ноября 2009

Интересная проблема, поэтому пара мыслей:

  • сначала: вы всегда можете использовать MI, чтобы Win32Dialog наследовал как от Win32Form, так и от Dialog, так как тогда вы получите хороший бриллиант, вам нужно виртуальное наследование
  • секунда: старайтесь избегать ИМ;)

Если вы хотите использовать определенные элементы управления, значит, вы используете метод, специфичный для Win32, верно? Я надеюсь, что вы не говорите о up_casting: /

Тогда как же этому специфическому для Win32 методу управления был передан универсальный объект, когда он мог бы получить более точный тип?

Возможно, вы могли бы применить Закон Деметры здесь. Который прекрасно работает с подходом Facade.

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

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

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

С другой стороны: Facade фактически означает раздутый интерфейс ...

0 голосов
/ 17 ноября 2009

Во-первых, основная проблема заключается в том, что в вашем слое "абстракции" нет абстракции.

Реализация тех же элементов управления, которые предоставляются ОС, и простое добавление префикса «Win32» к их именам не является абстракцией.

Если вы на самом деле не собираетесь упрощать и обобщать структуру, зачем вообще писать слой абстракции?

Чтобы это действительно было полезно , вы должны написать классы, представляющие понятия , которые вам нужны. И затем внутренне сопоставьте их с необходимым кодом Win32. Не создавайте класс Form или Window только потому, что в Win32 он есть.

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

Вместо функции

Form::addControl(Control &ctrl)

определить

template <typename Ctrl_Type>
Form::addControl(Ctrl_Type &ctrl)

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

Коллекция может хранить даже что-то вроде boost::variant объектов, что позволит вам сохранять некоторую информацию о типах даже там.

В противном случае, если вы идете по маршруту ООП, по крайней мере, сделайте это правильно . Если вы собираетесь скрыть и EditBoxes, и CheckBoxes за общим IControl интерфейсом, то это должно быть потому, что вам не нужен точный тип. Интерфейс должен быть всем, что вам нужно. В этом смысл интерфейсов.

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

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

Итак, мой совет: начните с того, как бы вы хотели, чтобы ваш GUI-слой работал. И затем беспокоятся о том, как это должно быть сопоставлено с базовыми примитивами Win32. Забудьте, что вы когда-либо слышали о формах, диалоговых окнах, окнах редактирования или даже элементах управления или окнах. Все это концепции Win32. Определите понятия, которые имеют смысл в вашем приложении. Нет правила, согласно которому каждый объект в графическом интерфейсе должен происходить из общего класса или интерфейса Control. Нет правила, что должно быть одно «Окно» или «Холст» для всего, что нужно разместить. Это соглашение, используемое Windows. Напишите API, который имел бы смысл для you .

А затем выясните, как реализовать это с точки зрения доступных примитивов Win32.

0 голосов
/ 17 ноября 2009

Я потратил годы на разработку мобильных телефонов.

Я рекомендую вам не попадать в ловушку «сделай абстракцию»!

Такие фреймворки - это больше кода (и утомительно, а не весело!), Чем просто реализация приложения на каждой платформе.

К сожалению, Вы ищете веселья и опыта, и я бы сказал, что есть места для веселья, чтобы получить его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...