Привет, друзья! Пишу программу и застрял вроде бы на простых вещах: есть форма где размещается четыре контрола, первые три это - выпадающий список (combobox), а последний это текстовое поле (textbox). Что необходимо: при выборе значения в первом combobox - во втором отображается список связный с первым значением, соответственно когда во втором combobox меняется значение, то в третьем combobox отображается список связный со вторым значением. Т.е. получается такая древовидная структура, но проблем в ее отображении нет. Проблема в том, что при выборе разных значений, должны произовиться разные действия.
Например:
Если первый combobox имеет "Значение_1", а второй combobox имеет "Значение_5", то третий combobox должен быть блокирован с выбором значения "Значение_7" и в textbox записано значение рассчитанное по алгоритму "Алгоритм_23", которое не может быть изменено вручную.
Т.е. получается что при выборе разных значений, должен не только меняться UI, но и также правила расчета алгоритма. Алгоритмы здесь не однотипные, которые можно абстагировать паттерном СТРАТЕГИЯ, а каждый имеет свои зависисмости(репозитории и т.д.).
UI реализован так: View работает с Presenter'ом, и например, при выборе значения из списка - вызывает его метод. В методе получается очень много if'ов, которые проверяют выбраны ли значения, если да, то какие. Комбинаций всех значений и действий связных с ними очень много. Действи касаются не только изменений отображаемой информации в UI, но и расчеты в бизнес-области. Так как работа метода зависит от состояния, я пытался применить паттерн состояние - как-то не очень понравилось, много действий повторяются и приходится придумывать имена каждому состоянию, например какое имя дать этому состоянию, когда выбраны значения: "Значение_1", "Значение_5" и "Значение_7"...
Видел программы, где на форме около 20 контролов, и все между собой "переплетены"... Думаю что должен быть какой-то общий подход к организации такой логики...
В первой таблице доступно все.
Во второй уже в зависимости от выбранного элемента с первой.
В третьей умножение первой таблицы на вторую дают все возможные комбинации, на против которых указан доступ к элементам.
Другими словами работа с множествами.
1-ое доступно целиком.
2-ое умножается на первое, и решение указано на пересечении.
3-е умножается на первое и второе множества, решение указано на пересечении.
Спасибо. Действительно интересный ответ, вопрос только в том что должно лежать на пересечении? Я так понимаю там может лежать метод(action/callback)? Верно?
Что там должно находится точно не скажу, нужно еще подумать.
Получается что в третьей таблице на пересечениях будут находится все возможные конечные варианты, при этом известны все три входных условия по которым мы туда попали. В целом это все что необходимо, дальше уже по требованиям, что там за алгоритм такой.
Да, таблицы мне кажется очень гибкое решение, про них я еще читал в книге "Совершенный код". Также искал что-то подобное в интернете, но к сожалению ничего толкового не нашел, а все что находил относилось к тестированию ПО, искал по запросу типа: "..таблицы решений..". Если продумать все так, чтобы это вписывалось грамотно во взаимодействие с UI, то было бы здорово. Если например, существуют 200 правил, то мы бы их просто описывали и добавляли в таблицу, а дальше уже какой-нибудь наш самописный движок исполнял код как надо, т.е. мы разгружаем наш мозг, и в основном работаем только руками добавляя правила в таблицу. Это похоже на то, как предлагал Ptolemy_master, чтобы уйти в сторону декларативности, но если мы создаем какой-то универсальный процессор обработки правил, то всегда появится куча "НО", которая заставит нас добавляет всякие исключительные случаи, тогда либо теряется универсальность, либо возрастает сложность!
Вообщем, я пока остановился на таком решении, буду использовать паттерн СОСТОЯНИЕ, где в роли стейт- машины будет Presenter, т.е. внешне View будет вызывать методы Presenter'а, но в методе не будет много разных if'ов, вместо этого он будет делегировать вызовы текущему объекту State...
Постарайтесь уйти от императивной логики к декларативной.
Скажем набор правил - это массив некоторых объектов, в полях которых вы задаете какие-то формулы и ссылки на другие объекты, данные которых надо использовать. И отдельно существует некая логика, которая читает эти объекты, читает элементы интерфейса и в соответствии с ними делает ветвления и вычисления. Достаточно один раз задать общие правила на чтение объекта и формирование действий и потом вам придется только задавать свои правил в массиве.
и как это решает проблему, когда на значение элемента влияют другие пять-шесть элементов, значения которых тоже зависят друг от друга по разным сценариям?
в вашем варианте - это все те же бесконечные комбинации if-else
ой всё, нет. это не бесконечные if-else, это очень ограниченный набор простых логических действий. Это как алфавит в языке, без него не обойтись, вы задаете правила, а что с ними делать, решит бизнес-логика алфавита (метаобъекта в данном случае).
Что касается ухода в сторону декларативности, то идея мне очень нравиться. Вопрос только в том что и где декларировать? Было бы здорово описать все связи и правила и отдать их какому-нибудь процессору, который бы весь этот комбайн запускал...
Допустим возьмем Веб, я там использовал для UI библиотеку VueJS, это наподобие React'а, так вот, там мы описываем декларативно графический интерфейс в зависимости от состояния объекта, когда меняется свойство объекта, то UI изменяется сам так, как ему надо без нашего вмешательства. Но тем не менее, зависимости свойства от других свойств и их значений - остается. Если мы меняем третье свойство, что должно произойти если оно зависит от первых двух и их конкретных значений? Опять в методе получается много if'ов...
ddd329, вы описали шаблон MVVM, если не ошибаюсь, но это связь модель-представление.
Вам же нужно создать связь модель-логика.
Попробуйте сначала рассмотреть все возможные варианты ветвлений и расчетов.
Например, в вашем случае - в примере - у вас такая логика:
Условия
a== 1
b == 5
c == 7
Сделать:
cBlocked = true
t = f(...некая функция)
То есть можно где-то просто перечислить условия, также где-то перечислить функционал, который будет выполняться, когда эти условия выполнены, удобно использовать указатели на функции.
Без ифов, конечно, не обойтись, но задавать и обрабатывать их будете не вы, а процессор, который пройдет через все ваши условия и проверит, выполняются они или нет.
В самом простейшем исполнении это может быть массив объектов, у которого одно поле - это массив условий, другое поле - ссылка на функцию, которая должна быть выполнена.
Можно запускать процессор каждый раз, когда меняется значение любого из контролов, а можно сделать предобработку - каким-то образом связать каждый контрол с конкретными условиями (то есть чтобы процессор проверять не все из них, а только те, в которых участвует данный контрол (ну или связанная с ним переменная), но это уже для оптимизации).
Если условия сложнее, или есть какие-то промежуточные условия и переходы, то можно добавить ссылки на условия, которые также должны проверять.
Попробуйте начать с построения процессора для самых простейших случаев, постепенно добавляя более сложные условия и переходы.
Такой подход называется реактивное программирование. и реализован в библиотеках Rx.NET и ReactiveUI. Бизнес-логику он вам проще не сделает, но организовать зависимости между тем какие значения как вычисляются - поможет (благодаря декларативному подходу к описанию зависимостей).