Как лучше всего избегать написания раздутого кода GUI?

48

Я нахожу, что всякий раз, когда я работаю с кодом GUI, код, как правило, раздувается быстрее, чем другие виды кода. Это также кажется сложным для рефакторинга. Принимая во внимание, что в других видах кода я могу довольно легко выполнить рефакторинг - я нахожу, что могу разложить большой класс на более мелкие части функциональности - с большинством каркасов GUI я часто привязан к каркасу, который требует, чтобы мой виджет / control / любой класс реализовать намного больше вещей непосредственно в виджете / элементе управления / что угодно. Иногда это происходит из-за необходимости (а) наследования какого-либо базового виджета / элемента управления / вещи или (б) доступа к защищенным методам.

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

  1. щелчок правой кнопкой мыши / контекстное меню
  2. реагировать на выбор из контекстного меню - может быть много
  3. особый способ рисовать графический интерфейс
  4. реагировать на ввод с клавиатуры
  5. кнопки, флажки,
  6. и т. д.

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

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

Есть ли способ управлять кодом GUI разумным способом и не допустить, чтобы он стал разбитым окном? Или масса обработчиков случайных событий / переопределенных методов действительно лучшее, что мы можем сделать для кода GUI?

Дуг Т.
источник
4
Каково ваше точное определение «раздувать»?

Ответы:

36

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

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

Три метода рефакторинга, которые я нахожу наиболее часто используемыми, это Rename , Extract Method и Extract Class . Если бы я никогда не узнал ни одного другого рефакторинга, эти три все равно позволили бы мне сохранить мой код чистым и хорошо структурированным, и из содержания вашего вопроса мне кажется, что вы, вероятно, обнаружите, что вы почти всегда используете те же три рефакторинга в чтобы ваш код GUI был тонким и чистым.

У вас может быть наилучшее в мире разделение между GUI и бизнес-логикой, и все же код GUI может выглядеть так, будто мой код был взорван в середине. Я советую, что не помешает иметь дополнительный класс или два, чтобы помочь вам правильно управлять вашим GUI, и это не обязательно должны быть ваши классы View, если вы применяете шаблон MVC - хотя часто вы найдете промежуточные классы настолько похожи на ваш взгляд, что вы часто будете испытывать желание объединить их для удобства. Мое предположение заключается в том, что на самом деле не мешало бы добавить дополнительный специфичный для графического интерфейса слой для управления всей визуальной логикой, однако, возможно, вы захотите взвесить преимущества и затраты, связанные с этим.

Поэтому мой совет:

  • Ничего не делать непосредственно за вашим GUI, кроме как вызывать и определять, как GUI будет подключаться к представлению (или промежуточному слою).
  • Не пытайтесь объединить каждую вещь, связанную с представлением, в один класс - или даже один класс для каждого окна графического интерфейса - если это не имеет смысла для вас. Ваша альтернатива - создать множество маленьких и простых в управлении классов для управления логикой GUI.
  • Когда ваши методы начинают выглядеть немного больше, чем 4-5 строк кода, проверьте, необходимо ли это и возможно ли извлечь один или два метода, чтобы вы могли сохранять свои методы простыми, даже если это означает класс со многими другими методами.
  • Если ваши классы начинают выглядеть действительно большими, начните с удаления ВСЕХ дублированных функций, а затем посмотрите, можете ли вы логически сгруппировать свои методы так, чтобы вы могли извлечь другой класс или два.
  • Подумайте о рефакторинге каждый раз, когда вы пишете строку кода. Если вы получите строку кода для работы, посмотрите, сможете ли вы ее реорганизовать, чтобы избежать дублирования функциональности или сделать ее немного более компактной без изменения поведения.
  • Примите неизбежное, что вы всегда будете чувствовать, что та или иная часть в вашей системе начнет чувствовать себя немного раздутой, особенно если вы пренебрегаете рефакторингом по ходу дела. Даже с хорошо разложенной кодовой базой, вы можете чувствовать себя , как будто есть больше , что вы могли бы сделать. Это реальность написания программного обеспечения, и вы всегда будете чувствовать, что что-то большее можно было бы сделать «лучше», поэтому вам нужно найти баланс между выполнением профессиональной работы и позолотой.
  • Согласитесь, что чем чище вы пытаетесь сохранить свой код, тем менее раздутым будет казаться ваш код.
S.Robins
источник
3
+1 Как ни крути, GUI заботится о миллионе подробных операций, а это значит код.
Патрик Хьюз
Разработчики должны научиться использовать событийно-ориентированное кодирование для GUI.
Дэвид Гао
23

Я думаю, что многие проблемы, с которыми вы сталкиваетесь, могут быть связаны с простой причиной. Большинство разработчиков не воспринимают код GUI как «настоящий» код. У меня нет никаких доказательств или статистики, только мое внутреннее чувство.

Может быть, они думают, что это « просто презентация » и не важно. « Там нет бизнес-логики », говорят они, « зачем это тестировать ?» Они смеются, когда вы упоминаете ориентацию объекта и пишете чистый код. Они даже не пытаются сделать вещи лучше. Не существует структуры, с которой нужно начинать, они просто бьют какой-то код и позволяют ему гнить, поскольку другие со временем добавляют свой собственный штрих. Прекрасный беспорядок, код граффити.

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

c_maker
источник
2
+1 за намеки на восприятие кода GUI, который обрабатывается иначе, чем код не GUI. Я потерял счет тому, сколько раз слышал, как кто-то говорит: «Не беспокойтесь о тестировании графического интерфейса, потому что это не выгодно и, к тому же, так сложно сделать». Я обычно перевожу: «Это сложно, и я слишком ленив, чтобы научиться это делать!».
С.Робинс
1
+1 Где я работаю, мы часто не пересматриваем код GUI - «это просто GUI, пропустите его». И я так же виновен, как и все остальные. Странно то, что в моих личных проектах я трачу много времени, пытаясь получить хороший чистый код GUI. Думаю, это просто вещь культуры.
HappyCat
8

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

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

Карл Билефельдт
источник
6

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

http://www.google.com/events/io/2009/sessions/GoogleWebToolkitBestPractices.html

Легко абстрагировать идеи от других рамок и языков. Основным преимуществом MVP (на мой взгляд) является юнит-тестируемость. И вы получите это только в том случае, если ваш код не раздут и не спагетти (судя по вашему вопросу, это то, что вы хотите). Это работает путем введения логического слоя представления, называемого презентатором. Фактическое представление отделено от этого через интерфейс (и, следовательно, может быть легко смоделировано в модульных тестах). Теперь, так как ваш логический уровень представления (презентатор) освобожден от внутренних компонентов конкретной структуры GUI, вы можете организовать его как обычный код, и вы не привязаны, например, к иерархии наследования Swings. В идеале вы могли бы переключать реализации GUI в разных средах, если они соответствуют одному и тому же интерфейсу.

scarfridge
источник
1
+1. MVP сосредотачивается именно на том, как извлечь логику GUI в отдельные классы, которая часто сильно отличается от того, что люди понимают, когда говорят о MVC.
Док Браун
5

Мой ответ состоит из четырех частей: структура, простота, тестирование и синтаксис.

Первые три действительно сложно сделать!

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

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

Средства тестирования, в том числе инструменты тестирования браузера (мне приходят в голову webrat и capybara в моей работе с рельсами), которые улавливают кросс-браузерные проблемы заранее, когда можно разработать лучший код для их решения в начале, а не частое «исправление» кода разными разработчиками, так как они «открыты» пользователями разных браузеров.

Синтаксис. Действительно полезно использовать средство проверки кода / IDE / редактор-плагин и т. Д. Для HTML, CSS, Javascript и т. Д. Преимущество, которое браузеры получили благодаря способности обрабатывать некорректно сформированный HTML, работает против вас, когда разные браузеры по-разному работают с это, так что инструмент, который проверяет ваш формат HTML имеет важное значение. Правильно сформированный HTML очень полезен при наличии неблокированного HTML, так как плохой код должен иметь большую видимость.

Майкл Даррант
источник
4

Решение, которое я нашел, - это декларативный код. Использование только процедурного кода - рецепт для спагетти-кода GUI. Конечно, «особый способ рисования виджета», вероятно, останется кодом. Но это код, изолированный в классе. Обработчики событий, сочетания клавиш, размеры окон - все эти грязные вещи лучше всего декларировать.

MSalters
источник
4

Здесь много хороших ответов.

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

В качестве простого примера: если у меня есть графический интерфейс с 4 полями для ввода текста, то у меня есть отдельный класс данных, в котором хранится содержимое этих 4 полей для ввода текста. Более сложные графические интерфейсы требуют больше классов данных.

Я проектирую GUI как модель - представление. Модель GUI контролируется контроллером приложения модели приложения - view - controller. Представление приложения - это модель GUI, а не сам код GUI.

Гилберт Ле Блан
источник
2

Такие приложения, как обработка текстов, графические редакторы и т. Д., Имеют сложные интерфейсы, и их код не может быть простым. Однако для бизнес-приложений графический интерфейс не обязательно должен быть таким сложным, как некоторые.

Вот некоторые из ключей к упрощению графического интерфейса (большинство относится к .NET):

  1. Стремитесь к более простому дизайну, когда это возможно. Избегайте причудливого поведения, если этого не требует бизнес.

  2. Используйте хорошего поставщика контроля.

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

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

  5. Используйте компонент (или структуру) для навигации.

  6. Создание стандартных диалогов для ошибок, предупреждений, подтверждения и т. Д.

Без шансов
источник
1

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

  1. Отдельное представление и модель Используйте любую библиотеку / среду MV или напишите свою собственную, чтобы помочь отделить логику вида / контроллера от модели данных. Все взаимодействие с бэкэндом должно осуществляться внутри модели, а состояние модели всегда должно быть синхронизировано с бэкэндом.
  2. Разъединение Если объект A знает об объекте B, то A может вызывать методы на B, но B не должен знать об A. Вместо этого A может прослушивать события из B. Это обеспечивает отсутствие циклической зависимости. Если в вашем приложении много событий между компонентами, создайте EventBus или используйте управляемую событиями среду, такую ​​как Twitter Flight.
  3. Частичное или полное рендеринг. Если ваше представление представляет собой таблицу или список элементов, у вас может возникнуть желание создать такие методы, как «добавить», «удалить», чтобы вставить / удалить один элемент в / из коллекции. Ваш код может легко раздуться, когда вы должны поддерживать сортировку и нумерацию страниц. Поэтому мой совет: просто перерисовать весь вид, даже если есть частичное изменение. Как насчет производительности? хорошо, если ваша коллекция большая, тогда вы все равно должны делать нумерацию страниц. Веб-разработчик: убедитесь, что ваши обработчики событий делегированы корневому элементу представления, который не изменяется.
  4. Модель представления Когда состояние вашего представления становится слишком сложным для поддержки, например, представление таблицы должно отслеживать данные строк, данные столбцов, порядок сортировки, проверенные в данный момент строки (если он поддерживает мульти-проверку) и т. Д., Вам, вероятно, следует создайте объект ViewModel для этих состояний. Ваш объект View должен вызывать сеттеры в ViewModel, если что-то меняется в пользовательском интерфейсе (например, пользователь проверяет строку); и он должен отвечать на событие изменения ViewModel путем обновления пользовательского интерфейса. Обычно следует избегать обновления пользовательского интерфейса, если событие изменения инициируется пользовательским интерфейсом.

Вот небольшое, но нетривиальное приложение, чтобы проиллюстрировать некоторые из моих моментов. Вы можете найти код и диаграмму взаимодействия вида / модели здесь: https://github.com/vanfrankie/pushpopbox

откровенный
источник
0

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

Существует поддержка связывания данных для многих сред пользовательского интерфейса, например .NET и Eclipse / JFace .

JesperE
источник