Название: Разделение логики и GUI Отправлено: Lagovas от Август 03, 2011, 19:25 Только начал осваивать Qt и тому подобное. Сразу хочется учится программировать правильно, ведь учиться легче чем переучиваться. Сейчас не работаю и не пишу продакшн проекты, а для себя. И мне кажется что в реальных приложениях намного лучше изначально отделять логику и отображение. Так вот, подскажите что почитать, что попробовать, в каком направлении двигаться. И возможно ли с Qt так делать. Насколько я пока понимаю как это делается, пишется в куте форма, с сигналами и методами которыми можно с ней взаимодействовать. А "логика" должна взаимодействовать с формой. Так вот, если так делать, то все классы логики должны наследоваться от QObject? Ведь вроде без него нельзя работать с сигналами, а если так, то вся фишка гуи в куте теряется. Скорее всего я чего то недопонимаю, и прошу раздуплить. Заранее благодарен.
Название: Re: Разделение логики и GUI Отправлено: Авварон от Август 04, 2011, 02:24 Так вот, если так делать, то все классы логики должны наследоваться от QObject? Ведь вроде без него нельзя работать с сигналами, а если так, то вся фишка гуи в куте теряется. Не понял. Какая фишка?Название: Re: Разделение логики и GUI Отправлено: Igors от Август 04, 2011, 06:02 Классическая проверка на вшивость: есть некоторое приложение с GUI, теперь требуется консольная версия (без GUI). И вот тут выясняется что "ой" :) По существу Вы спрашиваете: а как сделать чтобы мои приложения имели хорошую, гибкую архитектуру? Ответ на этот вопрос мне неизвестен. Я стараюсь решать это на уровне файлов и классов. Т.е. есть cpp файлы "с UI и без". Это примитивное разделение но работает неплохо. Расчетные" классы не знают о существовании UI и обычно подаются в UI как указатели/ссылки. Часто приходится делать небольшие адаптеры для развязки. Слот/сигнал цветет пышным цветом именно в UI, в расчетной части его применение гораздо скромнее, а часто можно спокойно обойтись и без него.
Тему Вы затронули интересную, но Ваш вопрос слишком общий. Возможно с конкретным примером обсуждение было бы более интересным/продуктивным. Название: Re: Разделение логики и GUI Отправлено: SeverusSnape от Август 04, 2011, 09:51 В качестве конкретного примера можно посмотреть на великолепную читалку FBReader - которая реализована по такому принципу
Название: Re: Разделение логики и GUI Отправлено: Lagovas от Август 07, 2011, 01:22 Просто есть ведь разные паттерны проектирования и тому подобное, много книг по архитектуре, думал уже есть какие то отлаженные механизмы разделения. Плюс хотел узнать возможно ли так и как вы это сами делаете.
Название: Re: Разделение логики и GUI Отправлено: asvil от Август 07, 2011, 09:59 Хм, web же. Там гуи на языке html + интерактивность с помощью javascript, для логики используется серверный язык (php, python, java ....). Вот вам шаблон проектирования: сервер логики и гуи клиент.
Распространенный шаблон номер два: фронт-енд к базе данных. ГУИ представляет из себя формочки для ввода данных, с некоторой интерактивностью, а тажке отчетики длы вывода данных. Сами же данные храняться в СУБД и агрегируются с помощью процедур в триггерах. Название: Re: Разделение логики и GUI Отправлено: Igors от Август 07, 2011, 11:43 Ну когда оно сделано и работает - то конечно все ясно :). А вот простейший пример: при выполнении какой-то ф-ии или метода произошла ошибка. Нужно показать пользователю модальный диалог и вернуть false
Код Как мы видим UI проникло/просочилось в cpp файл который занят разбором текста (т.е. никакого отношения к UI иметь не должен). Как этого избежать? Название: Re: Разделение логики и GUI Отправлено: Lagovas от Август 07, 2011, 11:47 Я хоть с потоками мало работал, но мне кажется можно ошибки писать в std::err или чет такое, а там где гуи, читать этот поток и выводить. На крайняк в файл, и с файла читать.
Название: Re: Разделение логики и GUI Отправлено: asvil от Август 07, 2011, 11:59 Lagovas прав.
QMessageBox в алгоритмах зло, ибо потенциально может являться спамом. Любой алгоритм должен журналироваться, а журнал перенаправлятся в виджет. Вот и вся логика. Если есть такое требование в ТЗ, то ТЗ нужно редактировать. Название: Re: Разделение логики и GUI Отправлено: Lagovas от Август 07, 2011, 12:06 А какой метод лучше, сделать отдельный метод в гуи классе, который принимает параметром ошибки для вывода и использовать его в логике, все же писать в поток и емитить сигнал, о том что надо показать еррор либо же что бы метод гуя был в отдельном потоке и по таймеру проверял еррор лог?
Название: Re: Разделение логики и GUI Отправлено: Igors от Август 07, 2011, 12:14 QMessageBox в алгоритмах зло, ибо потенциально может являться спамом. Все согласны что зло :)Любой алгоритм должен журналироваться, а журнал перенаправлятся в виджет. Вот и вся логика. Прошу показать "UI - независимую" версию фрагмента приведенного в посте #6Название: Re: Разделение логики и GUI Отправлено: asvil от Август 07, 2011, 12:14 Я реализовывал через log4qt с помощью сигнал-слотового соединения. Соединение потокобезопасное, поэтому как алгоритм будет реализован не важно.
Название: Re: Разделение логики и GUI Отправлено: asvil от Август 07, 2011, 12:22 Код: StaticLogAppender appender Название: Re: Разделение логики и GUI Отправлено: kambala от Август 07, 2011, 12:31 Прошу показать "UI - независимую" версию фрагмента приведенного в посте #6 например в Objective-C часто используется передача в функцию последним параметром указатель на NSError *, который в случае неудачи выполнения функции становится != nil и содержит информацию об ошибке, а в случае успеха - == nil.другой вариант - возвращать из функции QString, а не bool (в случае успеха - QString(), неудачи - сообщение с ошибкой), но тогда не получится красивого Код
Название: Re: Разделение логики и GUI Отправлено: Lagovas от Август 07, 2011, 12:36 разве не получится? Разве пустая строка не будет считаться false?
Название: Re: Разделение логики и GUI Отправлено: Igors от Август 07, 2011, 12:45 Код: connect(appender, SIGNAL(message), logwindow, SLOT(addMessage), Qt::QueuedConnection); Название: Re: Разделение логики и GUI Отправлено: asvil от Август 07, 2011, 13:06 Для интерактивности необходим дополнительный код. Могу дополнить вышеприведенный листинг. Только моя реализация приблизит архитектуру к разделению на консольную версию программы и гуи фронт-енда.
Название: Re: Разделение логики и GUI Отправлено: Igors от Август 07, 2011, 13:47 другой вариант - возвращать из функции QString, а не bool (в случае успеха - QString(), неудачи - сообщение с ошибкой), но тогда не получится красивого По-моему более естественно такКод А theErrorMgr находится в др файле и умеет работать с UI и без. В принципе если просто "текст ошибки" то проблем нет, и, на мой взгляд, задействовать qDebug() ни к чему. Но в том-то и дело что UI всегда имеет "мелкие подробности" которые специфичны и в общую схему упорно не укладываются :) (напр титул окна, иконка(и), какой-то (дополнительный) хелп текст, что-то надо выравнять направо и.т.п) Могу дополнить вышеприведенный листинг. Только моя реализация приблизит архитектуру к разделению на консольную версию программы и гуи фронт-енда. Ну если нетрудно, покажите (или расскажите в чем там смысл)Название: Re: Разделение логики и GUI Отправлено: lit-uriy от Август 07, 2011, 13:48 >>Как мы видим UI проникло/просочилось в cpp файл который занят разбором текста
>>(т.е. никакого отношения к UI иметь не должен). Как этого избежать? Я уже давно не использую "ничейных" функций, только классы. И использую, при необходимости, строковые переменные типа "последняя ошибка", метод класса возвращает ЛОЖЬ, при этом в вызывающем коде зовётся метод типа "Дай последнюю ошибку". Ну а дальше, зависит от того, является ли вызывающий код человеко-машинным интерфейсом (консольным или графическим, не важно) или нет. Название: Re: Разделение логики и GUI Отправлено: Igors от Август 07, 2011, 14:21 Я уже давно не использую "ничейных" функций, только классы. То ясно (см. мой предыдущий пост), толкуем о том что "текст ошибки" хорош для консоли, но (как правило) это еще не все что нужно UIИ использую, при необходимости, строковые переменные типа "последняя ошибка", метод класса возвращает ЛОЖЬ, при этом в вызывающем коде зовётся метод типа "Дай последнюю ошибку". Название: Re: Разделение логики и GUI Отправлено: asvil от Август 07, 2011, 14:23 Цитировать или расскажите в чем там смысл Смысл в том чтобы код ui и код алгоритма обменивались сообщениями в блокирующих/неблокирующих режимах в зависимости от потребностей. Код алгоритма вызывает write("Continue?"); if read() == n {stop}. Код ui показывает сообщение ну и, если оно требует ответа, ждет ответ. Ответ отправляется коду алгоритма.Название: Re: Разделение логики и GUI Отправлено: Авварон от Август 07, 2011, 15:03 делать классы реентрантными, возвращать ф-ией бул и иметь ф-ию lastError()
для асинхронных классов - сигнал эррор() Название: Re: Разделение логики и GUI Отправлено: Igors от Август 07, 2011, 15:21 Цитировать или расскажите в чем там смысл Смысл в том чтобы код ui и код алгоритма обменивались сообщениями в блокирующих/неблокирующих режимах в зависимости от потребностей. Код алгоритма вызывает write("Continue?"); if read() == n {stop}. Код ui показывает сообщение ну и, если оно требует ответа, ждет ответ. Ответ отправляется коду алгоритма.делать классы реентрантными, возвращать ф-ией бул и иметь ф-ию lastError() для асинхронных классов - сигнал эррор() Цитировать - почему ты ищешь только под фонарем? :)- да потому что только там светло! Название: Re: Разделение логики и GUI Отправлено: lit-uriy от Август 07, 2011, 15:22 Можно завести вспомогательный класс описывающий ошибку, удобный и для графического интерфейса и для текстового.
Название: Re: Разделение логики и GUI Отправлено: lit-uriy от Август 07, 2011, 15:25 >>нужен заголовок окна и тип иконки
заголовок, если это не имя приложения, то тип сообщения (ошибка, предупреждение, ...). А тип сообщения однозначно связан с картинкой. Из логики программы за ранее известно, что если сейчас функция вернёт ЛОЖЬ, то - ошибка (предупреждение,...). Название: Re: Разделение логики и GUI Отправлено: asvil от Август 07, 2011, 15:40 Цитировать Как Вы сообщите вызывающему оте "мелкие подробности" Енто называется протокол общения, в моем примере текстовый. Употреблять бинарный по желанию.lit-uriy класс то можно, вопрос в том как оптимальнее наследить в коде, что бы реализовать разделение мух и котлет, и чтобы и те и другие друг про друга знали. Название: Re: Разделение логики и GUI Отправлено: Igors от Август 07, 2011, 16:10 >>нужен заголовок окна и тип иконки Ну допустим, чуть сэкономили - вместо 2 параметров 1, но это так, "на спичках" - проблема-то остается. UI будет порождать десятки подобных параметров, отделаться просто "текстом ошибки" (a la Linux) никак не удастсязаголовок, если это не имя приложения, то тип сообщения (ошибка, предупреждение, ...). А тип сообщения однозначно связан с картинкой. Из логики программы за ранее известно, что если сейчас функция вернёт ЛОЖЬ, то - ошибка (предупреждение,...). Можно завести вспомогательный класс описывающий ошибку, удобный и для графического интерфейса и для текстового. Этот класс быстро превращается в "помойную яму" (типа Вындоуз реестра) в которую каждый конкретный интерфейс льет свое. В одном UI потребуется одно, в другом UI - другое. Получается что вызывающий должен знать все подробности всех интерфейсов - расписываемся в беспомощности.Цитировать Как Вы сообщите вызывающему оте "мелкие подробности" Енто называется протокол общения, в моем примере текстовый. Употреблять бинарный по желанию.lit-uriy класс то можно, вопрос в том как оптимальнее наследить в коде, что бы реализовать разделение мух и котлет, и чтобы и те и другие друг про друга знали. Название: Re: Разделение логики и GUI Отправлено: Lagovas от Август 07, 2011, 16:28 Много ли типов ошибок? Сделать какой нить доступный enum, и передавать как параметр. А вообще, разве при написании гуя, не ясно какие там могут быть ошибки? Заглавие делать обобщенным, типа Ошибка в работе с файлом, а в тексте уже подробности, которые предоставляет логика.
Название: Re: Разделение логики и GUI Отправлено: Igors от Август 07, 2011, 16:44 Много ли типов ошибок? Сделать какой нить доступный enum, и передавать как параметр. А вообще, разве при написании гуя, не ясно какие там могут быть ошибки? Заглавие делать обобщенным, типа Ошибка в работе с файлом, а в тексте уже подробности, которые предоставляет логика. Конечно "типов ошибок" совсем немного - но передавать этот параметр надо. Но наивно думать что отделаемся "текстом ошибки" + "типом ошибки" - это мы просто "воспроизвели ситуацию на следующем шаге". Очень быстро появляются "исключения" которые требуют 3-й параметр (следующий шаг). Когда число таких "шагов" переваливает за 8-10, думается типа "ну надо же что-то делать, как-то обобщать"Название: Re: Разделение логики и GUI Отправлено: kambala от Август 07, 2011, 16:52 можно попробовать паковать все в словарь, список, или строку с особыми разделителями
Название: Re: Разделение логики и GUI Отправлено: Lagovas от Август 07, 2011, 17:02 Лучше взять какой то лог проги, и посмотреть как это логируется. У сисадминов посмотреть и проанализировать)
А вообще логично сделать какой то базовый класс ерора, в котором описаны самые основные поля, которые могут потребоваться. И логика должна их использовать, заполнять по максимуму, а гуи использовать. Плюс здесь же будет возможность пронаследовать и добавить конкретные возможности которые нужны в частном случае. А вообще имхо преувеличиваете. Можно выделить группы ошибок, и от них отталкиваться. Ерроры которые не входят в группу, должны оформляться в ручную. Заглавие, текст и т.п. А самые частые сделать с значениями по умолчанию, имхо. Название: Re: Разделение логики и GUI Отправлено: Igors от Август 08, 2011, 07:19 можно попробовать паковать все в словарь, список, или строку с особыми разделителями Паковать тоже пробовал (и наблюдал как это делают другие :))Лучше взять какой то лог проги, и посмотреть как это логируется. У сисадминов посмотреть и проанализировать) Это мало поможет т.к. UI сисадминов не волнует.Для отработки простых (стандартных) ошибок я бы предложил такой вызов Код txt - все понятно errorID - информация для вызывающего errorClass - используется для установки типа алерта, иконки и.т.п. (общих атрибутов) Это покроет многие (простые) запросы расчетной части, но не все. Для запросов типа Yes/No/Cancel - др. ф-ция. Согласен что обобщать больше неэффективно - проще делать "прокладку" для каждого случая которых остается относительно немного. В общем, работы хватает :) Заметим что слот/сигнал в таких случаях нехорош. |