Часто в программах используют "Помощники" (wizard-ы).
Это несколько последовательно отображаемых пользователю окон, в которых он что-то вводит.
Последовательность окон может быть разная в зависимости от того, что вводит пользователь.
И управляется эта последовательность программой.
Хочется сделать "правильную" архитектуру для реализации такого Помощника.
С выделением классов: Представление, Контроллер, Модель, Презентатор в соответствии с "хорошими" принципами разработки ПО: типа CleanArchitecture, Domain-driven design и др.
Реализовывать планируется на WinForms C#.
С первого взгляда кажется, что для управления логикой и последовательностью отображения окон для пользователя, надо сделать какой-то отдельный объект-Контроллер.
Правильно?
E>С первого взгляда кажется, что для управления логикой и последовательностью отображения окон для пользователя, надо сделать какой-то отдельный объект-Контроллер.
Получается, что эта логика — она должна быть независима от конкретной реализации отображаемых окон.
И ее можно выделить даже в отдельный слой.
В книгах по архитектурам иногда кроме слоев Business-Layer и UI-Layer, также указывают Application-Layer.
Может быть эта логика, управляющая отображением окон Помощника, и есть часть Application Layer?
Здравствуйте, es3000, Вы писали:
E>Часто в программах используют "Помощники" (wizard-ы). E>Это несколько последовательно отображаемых пользователю окон, в которых он что-то вводит. E>Последовательность окон может быть разная в зависимости от того, что вводит пользователь. E>И управляется эта последовательность программой.
E>Хочется сделать "правильную" архитектуру для реализации такого Помощника. E>С выделением классов: Представление, Контроллер, Модель, Презентатор в соответствии с "хорошими" принципами разработки ПО: типа CleanArchitecture, Domain-driven design и др.
Пользователь такой системы кто?
Кто-то будет делать по 10 новых проектов на дню с "чистой" архитектурой?
Меня даже относительно неплохой wizard в Visual Studio выбешивает.
В смысле кто? Обычный человек.
Q>Кто-то будет делать по 10 новых проектов на дню с "чистой" архитектурой?
Зачем по 10 проектов?
В одном проекте можно же по разному реализовать какую-то функциональность.
В данном случае одну из функций приложения можно реализовать в виде Помощника.
Q>Меня даже относительно неплохой wizard в Visual Studio выбешивает.
Это да, "продвинутые" пользователи не очень любят wizard-ы.
Но насколько я знаю, те кто мало дружат с ПК — им Помощники нравятся.
Q>>Пользователь такой системы кто?
E>В смысле кто? Обычный человек.
Q>>Кто-то будет делать по 10 новых проектов на дню с "чистой" архитектурой?
E>Зачем по 10 проектов? E>В одном проекте можно же по разному реализовать какую-то функциональность. E>В данном случае одну из функций приложения можно реализовать в виде Помощника.
А, я понял, что затевается.
Вроде мастера каких-то там отчетов, определяемых пользователем программного продукта.
Тут определение отчета — это модель. А что тут контроллер и представдение — хз.
Если мастер — это цепочка модальных диалогов в классике жанра,то имхо не надо заморачиваться с MVC.
Недавно делал визард подключения к бд со всякими извращениями вроде upgrade версии бд и т.п.
Имхо тут лучше подходит конечный автомат (ака FSM), а не MVC.
В подобной задаче могу себе представить MVC только как "конструктор запросов" для построения отчета с квадратиками условий, как в MS Access.
Конечному пользователю все равно это очень больно будет...
Здравствуйте, es3000, Вы писали:
E>Хочется сделать "правильную" архитектуру для реализации такого Помощника. E>Что думаете?
Думаю, что с таким подходом, где у тебя "всё правильно", ты никогда ничего не напишешь. Упираться лбом в теорию можно бесконечно, но финальный код всё равно будет на костылях и изоленте вперемешку с хардкодом. Особенно, если отталкиваешься от "теории", ни черта не имея в "практике". Поучись писать хотя бы "плохие" программы — там увидишь, где можно писать "красиво", а где теория только мешает.
На мой взгляд использование MV* в WinForms — это натягивание совы на глобус. Если хотите попробовать современные паттерны разработки desktop GUI — попробуйте WPF & MVVM.
Ну и вообще, не слишком увлекайтесь "правильными" паттернами разработки. Очень много из того что нам "продают" на практике не особо используется или не особо удобно. Имхо, главным критерием должна быть понятность и легкость работы с кодом для других разработчиков. Покажите свой код другому разработчику — и если он скажет что все понятно и работать с таким дизайном (кода) удобно — это самое главное, и не надо париться, что вы не используете MV* или ещё что-то "модное".
MC>На мой взгляд использование MV* в WinForms — это натягивание совы на глобус. Если хотите попробовать современные паттерны разработки desktop GUI — попробуйте WPF & MVVM.
Я нигде не писал, что собираюсь использовать MVC.
Можно использовать и MVVM.
MC>Имхо, главным критерием должна быть понятность и легкость работы с кодом для других разработчиков. Покажите свой код другому разработчику — и если он скажет что все понятно и работать с таким дизайном (кода) удобно — это самое главное, и не надо париться, что вы не используете MV* или ещё что-то "модное".
А разве эти архитектуры не учат писать понятный и легкий код?
Здравствуйте, es3000, Вы писали:
E>Часто в программах используют "Помощники" (wizard-ы). E>Это несколько последовательно отображаемых пользователю окон, в которых он что-то вводит.
Сделай обычную форму, накидай на нее скрытых страниц визарда, отображай только одну страницу.
E>Последовательность окон может быть разная в зависимости от того, что вводит пользователь.
Т.к. последовательность сложная вынеси управление в отдельный класс WizardFlow.
Этот класс WizardFlow должен сообщать, какую страницу вывести при нажатии "вперед" и "назад".
При нажатии кнопки, обращайся к WizardFlow, и выводи соответствующую страницу (которую он укажет)
Логику для определения следующей/предыдущей страницы напишешь любую.
Но класс WizardFlow должен знать о некоторых "важных событиях", например, "пользователь выбрал такой-то ответ на такой странице".
Нужно классу WizardFlow сообщать о таких событиях.
Здравствуйте, es3000, Вы писали: E>А разве эти архитектуры не учат писать понятный и легкий код?
Нет архитектура — это не про написание кода.
Различные паттерны типа MVC — это способ разделить обязанности между классами.
Декомпозиция задачи на подзадачи на верхнем уровне абстакции.
Написание красивого и понятного кода также имеет отношение к декомпозиции, но на самом нижнем уровне.
Например, выделить похожий код в функцию-помощник, дать понятные имена переменным, не писать функции на сотни строки и т.п.
Если нужен mvvm с байндингами, типа как в WPF, но нужно в WinForms, есть вот такая штука. https://archive.codeplex.com/?p=truss
В простых случаях работает. В сложных не пробовал, но вроде как лаконично пишутся 'свои' байндинги.
Б>Т.к. последовательность сложная вынеси управление в отдельный класс WizardFlow.
Я тоже об этом думал.
Б>Этот класс WizardFlow должен сообщать, какую страницу вывести при нажатии "вперед" и "назад". Б>При нажатии кнопки, обращайся к WizardFlow, и выводи соответствующую страницу (которую он укажет)
Б>Логику для определения следующей/предыдущей страницы напишешь любую. Б>Но класс WizardFlow должен знать о некоторых "важных событиях", например, "пользователь выбрал такой-то ответ на такой странице". Б>Нужно классу WizardFlow сообщать о таких событиях.
Каким "компонентом" можно считать этот класс WizardFlow?
Это получается Презентатор?
Y>Если нужен mvvm с байндингами, типа как в WPF, но нужно в WinForms, есть вот такая штука. Y>https://archive.codeplex.com/?p=truss Y>В простых случаях работает. В сложных не пробовал, но вроде как лаконично пишутся 'свои' байндинги.
Спасибо! Посмотрю.
Но вопрос у меня был немного в другом.
Меня больше интересует архитектурное решение.
Здравствуйте, Буравчик, Вы писали: Б>Т.к. последовательность сложная вынеси управление в отдельный класс WizardFlow. Б>Этот класс WizardFlow должен сообщать, какую страницу вывести при нажатии "вперед" и "назад". Б>При нажатии кнопки, обращайся к WizardFlow, и выводи соответствующую страницу (которую он укажет)
Б>Логику для определения следующей/предыдущей страницы напишешь любую. Б>Но класс WizardFlow должен знать о некоторых "важных событиях", например, "пользователь выбрал такой-то ответ на такой странице". Б>Нужно классу WizardFlow сообщать о таких событиях.
Очень здравое предложение.
От себя добавлю, что еще бывает необходима логика пропуска некоторых страниц визарда, если по какой-то причине у нас уже достаточно информации.
При большом количестве страниц и ветвлений трудности будут с реализацией класса WizardFlow.
Запрограммировать его правильно в процедурном стиле на if-ах и switch-ах вызовет сложности.
Например, вот такая схема работы визарда у меня была в недавней задаче:
Каждому состоянию визарда я сопоставил класс WizardState, содержащий логику переходов в следующее и предыдущее состояние.
Объекты класса WizardState константные и существуют в единственном экземпляре (singleton).
Класс WizardFlow получился простым, менее 100 строк кода.
Он работает по принципу конечного автомата, каждый момент имея один текущий WizardState.
У него всего три операции:
получить начальную страницу
получить следующую страницу
получить предыдущую страницу
Q>Класс WizardFlow получился простым, менее 100 строк кода. Q>...
А если перевести это в терминологию "чистой архитектуры", то что в это реализации будет являться Представлением, Презентатором, контроллером, Интерактором?
Здравствуйте, es3000, Вы писали:
E>А если перевести это в терминологию "чистой архитектуры", то что в это реализации будет являться Представлением, Презентатором, контроллером, Интерактором?
Все это очень смахивает на натягивание совы на глобус...
Если очень грубо, то WizardFlow более похож на контроллер или презентатор.
Представление тут диалоговое окно мастера со всеми его страницами и контролами.
В моем случае использовался объект WizardData, который определенно можно рассматривать как модель.
С другой стороны, у меня не использовались архитектурные ограничения, характерные для MVC или MVP.
Так код страниц визарда (т.е. Представления) напрямую писал изменения в WizаrdData (т.е. модель).
Объект WizardFlow контролировал только один аспект поведения — переходы между страницами, но никак не использовался для внутренней логики работы каждой конкретной страницы.
Наверное, я просто не делал слишком сложные страницы, чтобы их логику мне захотелось выделить в какой-то отдельный класс.
Здравствуйте, qaz77, Вы писали:
Q>Все это очень смахивает на натягивание совы на глобус...
Да нет. Это и есть MVP, которое так сильно ищет es3000
Q>Если очень грубо, то WizardFlow более похож на контроллер или презентатор. Q>Представление тут диалоговое окно мастера со всеми его страницами и контролами. Q>В моем случае использовался объект WizardData, который определенно можно рассматривать как модель.
model — WizardData
presenter — код формы визарда, часть этого кода вынесена в отдельный класс WizardFlow
view — дочерние страницы визарда
Q>С другой стороны, у меня не использовались архитектурные ограничения, характерные для MVC или MVP. Q>Так код страниц визарда (т.е. Представления) напрямую писал изменения в WizаrdData (т.е. модель).
Да, по-хорошему отдельные страницы визарда должны отправить нужные сообщения, а код диалогового окна должен изменить WizаrdData.
Но это как раз пример частого упрощения применения теории для удобства работы.
Такое упрощение может аукнуться, а может и нет (зависит от объема кода)
Б>Да нет. Это и есть MVP, которое так сильно ищет es3000
Да нет, я не ищу именно MVP.
Я ищу способ написать "хороший", поддерживаемый, расширяемый код.
Чтобы в нем разные зоны "ответственности" были разделены.
В этом есть здравая идея.
Но на практике сталкиваешься с тем, что разделить-то толком и не получается.
Все равно получается (хоть "чистая архитектура", хоть MVP, хоть MVVP), что все друг на друга "завязано".
Смысл разделения в чем?
В том, чтобы при изменении одной части кода, не приходилось менять другую часть кода.
Например, представьте тот же Wizard.
Мы хотим отвязать логику отображения последовательности форм от конкретных форм-представлений.
Например, чтобы иметь возможность позже сделать формы красивее.
И получается, что мы не можем этого сделать!
Так как логика отображения форм заложена в Презентере, а Презентер жестко завязан на формы.
Как их отвязать?
Здравствуйте, es3000, Вы писали: E>Да нет, я не ищу именно MVP.
E>Я ищу способ написать "хороший", поддерживаемый, расширяемый код. E>Чтобы в нем разные зоны "ответственности" были разделены. E>В этом есть здравая идея.
Все правильно. И изучать имеющиеся подходы также надо.
Но умение правильно расставлять приоритеты и искать компромиссы приходит с опытом...
E>Но на практике сталкиваешься с тем, что разделить-то толком и не получается. E>Все равно получается (хоть "чистая архитектура", хоть MVP, хоть MVVP), что все друг на друга "завязано".
Чтобы что-то работало совместно, оно должно быть внутри как-то завязано.
Обычно, когда говорят про отсутствие зависимости или слабую зависимость, то имеют в виду зависимость от конкретной реализации объекта-контрагента.
Т.е. для нас объект выполняет какую-то роль, на которую мы можем полагаться.
Обычно такая роль оформляется в виде абстрактного интерфейса, операции которого наделены определенными ожиданиями (семантикой).
Допустим, я прихожу в супермаркет, беру бутылку молока и иду с ней на кассу.
Мне не важно, кто сидит за кассой, тетя Маша или тетя Люба.
Я протягиваю бутылку молока, вызывая операцию "пробить товар" у абстрактного интерфейса "кассир" и ожидаю продолжения протокола взаимодействия (прием оплаты и т.д.).
Нарушением семантики операции в данном случае, например, будет, если кассирша вместо положенного, откупорит бутылку и выпьет молоко.
Все объекты тут те же (содержание диаграммы классов тождественное), а разница поведения очевидна.
Когда говорят о минимизации зависимостей, то подразумевают, что нам не важно какой именно человек сидит за кассой, лишь бы он делал то, что положено интерфейсу "кассир".