Имя: Пароль:
1C
 
Регистры правил или проклятие вариативности.
0 Гений 1С
 
гуру
31.10.06
18:59
Сформулируем проблему в общем виде.
Допустим, есть некоторые параметры, например Подразделение, Вид подразделения, Уровень подразделения, Товар, Вид товара.
И допустим для этих параметров нужно вычислять некоторые фунцкии, например Прибыль, Доход, Затраты, Маржа и т.п.

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

В итоге получается такой код:

Если УровеньПодразделения=… Тогда
  Прибыль=...;
  Доход=…;
Иначе

   Если Подразделение=... Тогда
      Прибыль=...;
   ИначеЕсли Подразделение=… И УровеньПодразделения=… Тогда
      Прибыль=...;
   ИначеЕсли Подразделение=… Тогда
      Прибыль=...;
   КонецЕсли;
КонецЕсли;

В общем случае получается офигенно длинный и запутанный код.

Кто-нибудь применял в своей программистской практике альтернативу, или смирился с этим, как со злом?

Я лично запарилси...
1 Волшебник
 
31.10.06
19:30
Могу порекомендовать психолога, он изменит твоё отношение к такому коду и ты будешь считать это добром. Следовательно, проблема будет решена.
2 avmlvm
 
31.10.06
19:33
(1) А почему "психолога"? чЁ "Волшебника" для этого не достаточно? :-)))
3 nbIx
 
31.10.06
19:36
(0) Про какую альтернативу ты говоришь?
Можно конечно ингда уменьшить количество Если Тогда, но все зависит от конкретной задачи.
4 avmlvm
 
31.10.06
19:36
(0) Если функции считаются по разному для разных параметров - то это РАЗНЫЕ функции... Т.е. Задача сводится, что бы по типу подразделения вызвать ПРАВИЛЬНУЮ функцию... Ну и чЁ тут сложного? :-)
5 Темный Эльф
 
31.10.06
19:38
Можно сделать справочник формул, задавать в реквизитах параметры и способы расчета, а затем просто искать по справочнику. У меня был отчет, в котором каждый месяц какие-то графы переносились со страницы на страницу, графы добавлялись, менялась ширина и положение колонок - так через полгода задолбался переделывать и каждый раз исправлять свои глюки, а сделал справочником настройки и сказал бухгалтеру - "Приспичило? Берите и меняйте!"
6 nbIx
 
31.10.06
19:43
(5) Да. Чем больше универсальности, тем лучше.
7 Гений 1С
 
гуру
31.10.06
19:45
(4) окей, это будет туча разных функций, но от этого количество Если не уменьшится....

Хотя на самом деле вот тебе контрпример.
Функция Доступ(Пользователь, Объект, ВидДоступа), которая определяет имеет ли пользователь доступ к объекту.
Функция одна, а вариантов - туева хуча!
8 Гений 1С
 
гуру
31.10.06
19:45
(5) не то, не то... алгоритмы известны, но настолько много вариантов, что все выходит из-под контроля!
9 Темный Эльф
 
31.10.06
19:53
(8)Насколько много? Можно Шаблон использовать, можно вообще текст модуля создавать, а потом по ЗагрузитьИзФайла вызывать.
10 Вуглускр
 
01.11.06
07:15
(0) Не знаю как это будет по-русски, по-английски то что тебе нужно называется
11 Вуглускр
 
01.11.06
07:32
опаньки, не ту кнопку нажал
Короче, Design Patterns. Классическая книга на эту тему написана GoF. Вот очень хорошая книга, причем бесплатно - "Thinkig in Patterns", www.bruceeckel.com
12 Гений 1С
 
гуру
01.11.06
10:05
Поясню на примере прав доступа, хотя у меня сейчас задача по бюджетированию, но по правам тоже заморочки бывают.
Функция Доступ(Пользователь, Объект, ВидДоступа)

   Если НаборПрав="Админ" Тогда //1
       Возврат Истина;
   КонецЕсли;
   //Пользователю запрещено редактировать дату //2
   Если НаборПрав="Пользователь" И Объект.Тип="ПриходнаяНакладная" Тогда
       Возврат ложь;
   КонецЕсли;
   //Контроль даты //3
   Если НаборПрав="Пользователь" ИЛИ Набор="Оператор" Тогда
       Если текущаяДата-Объект.Дата()>24*3600 Тогда
           Возврат ложь;
       КонецЕсли;
   КонецЕсли;
   ...

КонецФункции

т.е. как видим нужно правильно указывать последовательность, потому что если мы перепутаем местами 2 и 3, результат будет другой...
13 Defender aka LINN
 
01.11.06
10:18
(12) Ж8-[ ] Это у тебя и правда такое в конфе?
Жесть...
14 Гений 1С
 
гуру
01.11.06
10:45
(13) да
15 Гений 1С
 
гуру
01.11.06
10:45
Примерно
16 Господин ПЖ
 
01.11.06
10:58
Акуеть просто...
17 asssa
 
01.11.06
11:03
А рекурсия не поможет?
18 Neco
 
01.11.06
12:15
Ты же сам идеи толкал как в 1Ске Exel сделать. Ну так вперед. Реализуй.
19 Гений 1С
 
гуру
01.11.06
12:44
(17) тут ваще все слишком сложно, рекурсия - не поможет...
20 Гений 1С
 
гуру
01.11.06
12:45
(18) а каким боком здесь эксель поможет...
это фундаментальный вопрос
21 Salvador Limones
 
01.11.06
12:47
(20) Тебя это беспокоит? Хочешь поговорить об этом?
22 Гений 1С
 
гуру
01.11.06
12:50
обобщенно вопрос можно сформулировать так.

Есть некая функция F(A1, A2, ..... AК).
Эта функция принимает различные значения при различных наборах А1....АК.
Значения могут повторяться. В случае функции Доступ таких значений вообще только 2 - истина или ложь.

Возможна реализация следующим образом - составляется таблица различных значений А1.....АК и пишется для них значения.
Но в общем случае таблица может быть офигенно большой.

Как минимизировать таблицу, исходя из того, что мы знаем много частных случаев, когда принимаются те или иные значения при различных наборах параметров?
23 Бриарей
 
01.11.06
13:07
(22) Полиморфизм тебе поможет
24 Гений 1С
 
гуру
01.11.06
13:21
(23) я думаю, мне поможет регистр правил... ;-(
25 Гений 1С
 
гуру
01.11.06
13:21
еще бы сообразить, как его соорудить.
26 Asmody
 
01.11.06
13:25
(25) гугл, первая строчка в выдаче: http://www.kint.ru/k.pl?p=110
27 Гений 1С
 
гуру
01.11.06
13:26
(26) это я уже читал, только это не очень то применишь к моему случаю. ;-)
28 Asmody
 
01.11.06
13:28
(27) да ладно. если и умом и руками подойти, на регистрах сведений можно реализовать регистры правил...
29 Гений 1С
 
гуру
01.11.06
13:28
(28) я не про то, что реализовать нельзя, просто не очень поможет наличие регистров правил. ;-)
30 Бриарей
 
01.11.06
13:42
(29) РП предназначен для хранения детерминированных корней. В данной задаче корень (прибыль, доход и т.д.) не детерминирован.
Юзай полиморфизм.
31 Scooter
 
01.11.06
13:47
Смотри как в УПП бюджетирование сделано
перенеси подсистему если нужно
32 Wasya
 
01.11.06
13:58
(0) Сам спрашиваешь и сам отвечаешь. Так делай регистр правил.
33 Neco
 
01.11.06
14:42
+(31) Система финансовых расчетов (Справочник финансовые расчеты + Отчеты)
34 Гений 1С
 
гуру
01.11.06
16:22
Я просто продемонстрирую код, чтобы вы поняли, как я извращаюсь.
Итак функция Показатель(П) вызывается для каждой ячейки отчета.
П.Строка - ссылка на строку отчета
П.Колонка - ссылка на строку отчета.

Каждая строка и колонка отчета имеют уникальный идентификатор.
П.Строка.Схема и П.Колонка.Схема - структуры, заполняются из таблицы, где напротив каждого идентификатора стоит список параметров, таких как ВидИтога, ФормулаИтога, ФормулаПростая, ФормулаАгрегатная и т.п.

Получается нехилый если - то...

Функция Показатель(П)
   Перем Показатель, Р;
   Перем С;
   
   Если НЕ П.Дерево.Данные.Свойство("ПараметрыВычислений") Тогда
       ДанныеПараметрыВычислений(П);
       П.Дерево.Данные.Вставить("ПараметрыВычислений", истина);
   КонецЕсли;
   С=П.Дерево.Данные;
   
   //    Агрегат - используется для указания, что отчет представляет собой агрегат подотчетов
   //    ЛистыСуммированияФакта
   //    ЛистыСуммированияПлана
   //    ЛистыСуммированияБюджета
   
   
   //Смотрим, какой показатель мы расчитываем
   Показатель=П.Строка.Схема.Идентификатор;    

   //Определяем колонку
   Колонка=П.Колонка.Схема.Идентификатор;

   //Если итог по колонке считается по другому, указать специфику здесь
   
   //Для Колонки итого
   Если Колонка="ИтогПоКолонке" Тогда
       
       Если П.Строка.Схема.ВидИтога="Сумма" Тогда
           Возврат фоСумма(П,"@КолонкаМесяца");
       КонецЕсли;
       
       //Берется значение последнего месяца
       Если П.Строка.Схема.ВидИтога="Последний" Тогда
           Возврат фоЗнач(П, "@М12");
       КонецЕсли;
       
       //Берется значение первого месяца
       Если П.Строка.Схема.ВидИтога="Первый" Тогда
           Возврат фоЗнач(П, "@М1");
       КонецЕсли;
       
       //Берется среднее значение по месяцам
       Если П.Строка.Схема.ВидИтога="Среднее" Тогда
           Возврат фоОКДеление(фоСумма(П,"@КолонкаМесяца"), 12,2);
       КонецЕсли;
       
       //А может быть есть явная формула итога?
       Если П.Строка.Схема.ФормулаИтога<>Неопределено Тогда
           Возврат ВычислитьФормулу(П, П.Строка.Схема.ФормулаИтога);
       КонецЕсли;
       
       //Иначе колонка итого считается так же, как и помесячная колонка
   ИначеЕсли Колонка="ФактПрошлогоГода" ИЛИ Колонка="ПланПрошлогоГода" Тогда
       //Простое суммирование за прошлые месяцы
       //Если фоКатегория("УВР_Департамент;УВР_Управление", П.ОбщийВидОтчета) Тогда
       //    Листы=ДанныеЛисты;
       //КонецЕсли;
       //Если нужно суммирование данных подотчетов
       Если С.Агрегат Тогда
           //Смотрим, определена ли формула для агрегатных отчетов
           Формула=П.Строка.Схема.ФормулаАгрегат;
           Если Формула<>Неопределено И Формула<>"Сумма" Тогда
               Возврат ВычислитьФормулу(П, Формула);
           КонецЕсли;
           
           //Если формула не определена, но этот показатель подвержен суммированию
           //то считаем как обычную сумму листов книги
           //Или если явно указано, что нужно суммировать
           //Тогда суммируем подотчеты
           Если Формула="Сумма" ИЛИ фоЭтоКатегория("Сумма;Последний;Первый", П.Строка.Схема.ВидИтога) Тогда
               //Определяем, какие листы книги суммировать
               Листы=?(Колонка="ФактПрошлогоГода", С.ЛистыСуммированияФакта, С.ЛистыСуммированияПлана);
               
               //Если значение не заполнено, то оно не будет и учитываться
               Возврат ДанныеСуммаЛистов(П, П.Строка.Идентификатор, П.Строка.Значение, , Листы);
           КонецЕсли;
       КонецЕсли;
       
       //Смотрим, есть ли формула для вычисления факта/плана, если есть, она и используется
       Формула=П.Строка.Схема[Колонка];
       Если Формула<>Неопределено Тогда
           Возврат ВычислитьФормулу(П, Формула);
       КонецЕсли;
       
       //Иначе данные по факту/плану считаются по обычной схеме месяца, по обычной формуле для месяца
   КонецЕсли;
   
   //Частные случаи по месяцам описываются здесь
   
   //В противном случае колонка итого расчитывается так же, как и месячный показатель
   Формула=П.Строка.Схема.ФормулаПростая;
   Возврат ВычислитьФормулу(П, Формула);
КонецФункции

Функция ВычислитьФормулу(П, Формула, Р=Неопределено)
   Если Формула="-" Тогда
       Возврат Неопределено;
   КонецЕсли;
   Возврат обВыполнитьФормулуСПараметром(Формула, П, Р);
   Возврат Р;
КонецФункции
35 Гений 1С
 
гуру
01.11.06
16:23
(33) слишком простая модель для нашей конторы
36 ОбычныйЧеловек
 
01.11.06
16:31
(Гений 1С) если тебе "регистр правил" не поможет то тебе уже ничего не поможет.
37 France
 
01.11.06
19:45
я бы пошел как в (30).. классический случай..