Я должен взять часть данных и применить большое количество возможных переменных к ним. Мне действительно не нравится идея использовать гигантский набор того, если операторы, таким образом, я ищу справку в подходе для упрощения, и помогают поддержать.
Как пример:
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 различных тестов и изменений формулы.
Это просто походит на кошмар обслуживания. Какие-либо предложения?
Править:
Для дальнейшего добавления к проблеме много переменных только используются в определенных ситуациях, таким образом, это - больше, чем просто фиксированный набор логики с различными значениями. Сама логика должна измениться на основе условий, возможно условия, примененные от предыдущих переменных (если val> порог, например).
Таким образом да, я соглашаюсь об использовании поисков для многих значений, но у меня также должна быть переменная логика.
Обычный способ избежать больших коммутационных структур - поместить информацию в структуры данных. Создайте перечисление SportType
и Dictionary
, содержащие связанные значения. Вы можете просто написать 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];
}
}
Cribbing от The Pragmatic Programmer, вы можете использовать DSL для инкапсуляции правил и написания механизма процесса. Для представленной проблемы решение может выглядеть так:
MATCH{
Soccer soccerBaseVal
IsMale 5
!IsMale 1
}
SWITCH{
Monday 12
Tuesday 13
}
Затем сопоставьте все в первом столбце MATCH и первом элементе в каждом SWITCH, к которому вы пришли. Вы можете создать любой синтаксис, который вам нравится, а затем просто написать небольшой скрипт, чтобы втиснуть его в код (или использовать Xtext, потому что он выглядит довольно круто).
Используйте оператор switch или функцию фильтрации.
Под функцией фильтра я имею в виду что-то вроде:
func filter(var object, var value)
{
if(object == value)
object = valueDictionary['value'];
}
Затем примените фильтр с помощью:
filter(theObject, soccer)
filter(theObject, football)
Обратите внимание, что фильтр работает намного лучше с использованием словаря, но это не обязательно.
В качестве первого шага я, вероятно, разбил бы каждую область логической обработки на отдельный метод: (Может быть, это не лучшие имена в качестве первого прохода)
EnforceSportRules
ProcessSportDetails
EnforceGenderRules
Затем, в зависимости от того, насколько сложны правила, я могу нарушить каждую раздел в свой собственный класс и обработайте их основным классом (например, фабрикой).
GenderRules
GenderContext
Вот несколько идей:
1 Используйте таблицы поиска:
var val = 0;
SportType sportType = GetSportType();
val += sportvalues[sportType];
Вы можете загрузить таблицу из базы данных.
2 Используйте заводской шаблон:
var val = 0;
val += SportFactory.Create(sportType).CalculateValue();
Динамический заводской шаблон полезен в ситуациях, когда в код часто добавляются новые (спортивные) типы. Этот шаблон использует отражение, чтобы предотвратить изменение фабричного класса (или любой глобальной конфигурации). Это позволяет вам просто добавить новый класс в ваш код.
Конечно, использование динамической фабрики или даже фабрики может оказаться излишним в вашей ситуации. Ты единственный, кто может сказать.
Если вы действительно просто добавляете значения в этом виде, я бы либо создал перечисление с определенными индексами, которые соответствуют сохраненным значениям в массиве. Затем вы можете сделать что-то вроде этого:
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 */;
}
У меня нет ничего особенного, чтобы предложить вам, кроме как сначала рекомендовать не оставлять его просто большим блоком - разбейте его на разделы, сделайте разделители комментариев между важными частями.
Еще одно предложение: если вы собираетесь проводить много очень коротких тестов, как в вашем примере, отойдите от соглашения и поместите инкременторы val в одну строку с вычислением и отступом, чтобы они совпадали друг с другом.
if (isSoccer) val = soccerBaseVal;
if (isMale) val += 1;
else val += 5;
switch(dayOfWeek){
case DayOfWeek.Monday: val += 12;
...
}
Избыточные пробелы могут превратить эти сотни элементов в несколько сотен строк, что сделает вертикальную прокрутку чрезмерной и затруднит получение общего представления.
Рассмотрите возможность реализации шаблона стратегии , который использует наследование / полиморфизм, чтобы сделать управление отдельными функциями разумным. Разделив каждую функцию на отдельный выделенный класс, вы можете избавиться от кошмара длинных блоков case
или if
операторов.
Не уверен, поддерживает ли это C # еще (или когда-либо будет), но VB.NET интегрирует директивы XML Comment CompletionList в intellisense, что - в сочетании с шаблоном стратегии - может дать вам легкость использование Enum с неограниченной расширяемостью OO.