Лучший подход к программированию очень сложных бизнес / математических правил - PullRequest
6 голосов
/ 29 июня 2010

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

Как пример:

if (isSoccer)
    val = soccerBaseVal;
else if (isFootball)
    val = footballBaseVal;
.... // 20 different sports

if (isMale)
   val += 1;
else
    val += 5;

switch(dayOfWeek)
{
    case DayOfWeek.Monday:
       val += 12;
    ...
}

и т. Д. И т. Д. И т. Д., Возможно, в диапазоне от 100 до 200 различных тестов и вариантов формул.

Это просто похоже на кошмар обслуживания. Есть предложения?

EDIT:

Чтобы еще больше добавить к проблеме, многие переменные используются только в определенных ситуациях, так что это больше, чем просто фиксированный набор логики с разными значениями. Сама логика должна меняться в зависимости от условий, возможно, условий, применяемых из предыдущих переменных (например, если val> threshold).

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

Ответы [ 8 ]

7 голосов
/ 29 июня 2010

Обычный способ избежать больших структур переключения - поместить информацию в структуры данных.Создайте перечисление SportType и Dictionary<SportType, Int32>, содержащие связанные значения.Вы можете просто написать val += sportTypeScoreMap[sportType] и все готово.

Вариации этого шаблона помогут вам во многих подобных ситуациях.

public enum SportType
{
    Soccer, Football, ...
}

public sealed class Foo
{
    private static readonly IDictionary<SportType, Int32> sportTypeScoreMap =
        new Dictionary<SportType, Int32>
        {
            { Soccer, 30 },
            { Football, 20 },
            ...
        }

    private static readonly IDictionary<DayOfWeek, Int32> dayOfWeekScoreMap =
        new Dictionary<DayOfWeek, Int32>
        {
            { DayOfWeek.Monday, 12 },
            { DayOfWeek.Tuesday, 20 },
            ...
        }

    public Int32 GetScore(SportType sportType, DayOfWeek dayOfWeek)
    {
        return Foo.sportTypeScoreMap[sportType]
             + Foo.dayOfWeekScoreMap[dayOfWeek];
    }
}
1 голос
/ 29 июня 2010

Вот несколько идей:

1 Использовать справочные таблицы:

var val = 0;

SportType sportType = GetSportType();

val += sportvalues[sportType];

Вы можете загрузить таблицу из базы данных.

2 Используйте заводской шаблон:

var val = 0;

val += SportFactory.Create(sportType).CalculateValue();

Динамический фабричный шаблон полезен в ситуациях, когда новые (спортивные) типы часто добавляются в код. Этот шаблон использует отражение, чтобы предотвратить изменение фабричного класса (или любой глобальной конфигурации). Это позволяет вам просто добавить новый класс в ваш код.

Конечно, использование динамической фабрики или даже фабрики может быть излишним в вашей ситуации. Ты единственный, кто может сказать.

1 голос
/ 29 июня 2010

Письмо от Pragmatic Programmer, вы можете использовать DSL для инкапсуляции правил и написания механизма процессов.Для представленной проблемы решение может выглядеть следующим образом:

MATCH{
    Soccer   soccerBaseVal

    IsMale   5
    !IsMale  1
}

SWITCH{
    Monday   12
    Tuesday  13
}

Затем сопоставьте все в первом столбце MATCH и первом элементе каждого переключателя, к которому вы пришли.Вы можете создать любой синтаксис, какой захотите, затем просто написать немного скрипта, чтобы встроить его в код (или использовать Xtext, потому что он выглядит довольно круто).

1 голос
/ 29 июня 2010

Используйте либо оператор switch, либо функцию фильтра.

Под функцией фильтра я имею в виду что-то вроде:

func filter(var object, var value)
{
    if(object == value)
        object = valueDictionary['value'];
}

Затем примените фильтр с:

filter(theObject, soccer)
filter(theObject, football)

Обратите внимание, что фильтр работает намного лучше при использовании словаря, но это не обязательно.

0 голосов
/ 29 июня 2010

Рассмотрите возможность реализации Стратегического паттерна , который использует наследование / полиморфизм, чтобы сделать управление отдельными функциями разумным.Разделяя каждую функцию на отдельный выделенный класс, вы можете отказаться от кошмарного количества case блоков или if операторов длиной в мили.

Не уверен, что C # пока поддерживает (или когда-либо будет), но VB.NET интегрирует директивы XML Comment CompletionList в intellisense, что - в сочетании с шаблоном стратегии - может дать вам простоту использования Enum с неограниченной расширяемостью OO.

0 голосов
/ 29 июня 2010

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

enum Sport
{
    football = 0,
    soccer   = 1,
    //...
}

int sportValues[] = { 
    /* footballValue */,
    /* soccerValue */,
    /* ...Values */
};

int ApplyRules(Sport sport, /* other params */)
{
    int value = startingValue;
    value += sportValues[(int)sport];
    value += /* other rules in same fashion */;
}
0 голосов
/ 29 июня 2010

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

Другое предложение, если вы собираетесьчтобы иметь много очень коротких тестов, как в вашем примере, выйдите из соглашения и поместите инкременты val в ту же строку, что и оценка и отступ, чтобы они совпали друг с другом.в несколько сотен строк, что делает вертикальную прокрутку чрезмерной и затрудняет общее представление о вещи.

0 голосов
/ 29 июня 2010

В качестве первого шага я, вероятно, разбил бы каждую область логической обработки на свой собственный метод: (Может не быть лучшими именами в качестве первого прохода)

EnforceSportRules
ProcessSportDetails
EnforceGenderRules 

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

GenderRules
GenderContext
...