В C#, шаблоны - много условий
Я ищу хороший шаблон для моей проблемы.
У меня есть некоторые переменные типа bool:
condition1, condition2, condition3.
также у меня есть некоторые действия, которые называются в разных местах внутри класса:
action1, action2, action3
Action1 вызывается, когда выполняются условия 1 и 2. action2 вызывается, когда выполняются условия 2 и 3. Действие 3 вызывается, когда выполняются все условия.
конечно, это просто упрощение проблемы. Я не хочу использовать if else в каждом месте. Это ужасно непонятно.
Я думал о состоянии, но я думаю, что это не лучшее решение для этой проблемы.
6 ответов
один из вариантов-обернуть логику условия в базовый класс, а затем вывести из него фактические действия. Это вариация на команда шаблон и (я думаю)стратегия шаблон:
class ActionState
{
public bool Condition1{get;set;}
public bool Condition2{get;set;}
public bool Condition3{get;set;}
}
abstract class ActionDispatcher
{
protected abstract void ExecuteAction1();
protected abstract void ExecuteAction2();
protected abstract void ExecuteAction2();
public void Action1(ActionState state)
{
if(state.Condition1 && state.Condition2)
{
ExecuteAction1();
}
}
public void Action2(ActionState state)
{
if(state.Condition2 && state.Condition3)
{
ExecuteAction2();
}
}
public void Action3(ActionState state)
{
if(state.Condition1 && state.Condition2 && state.Condition3)
{
ExecuteAction3();
}
}
public void AllActions(ActionState state)
{
// Execute all actions depending on the state
Action1(state);
Action2(state);
Action3(state);
}
}
Вам может помочь enum
с [Flags]
атрибут, а не отдельные логические значения. См.ответ для очень хорошего объяснения + примеры.
ваши условия не очень хорошо определены, но это звучит как карта состояний к действиям, где состояние определяется рядом простых условий, и каждое состояние имеет только одно действие. Так почему бы на самом деле не представить это так?
вот простой пример LinqPad:
void Main()
{
Dictionary<Cond, Action> d = new Dictionary<Cond, Action>()
{
{ new Cond(waterproof:true, shockproof:true, freezeproof:false), delegate() { "Action1".Dump(); } },
{ new Cond(waterproof:false, shockproof:true, freezeproof:true), delegate() { "Action2".Dump(); } },
{ new Cond(waterproof:true, shockproof:true, freezeproof:true), delegate() { "Action3".Dump(); } }
};
d[new Cond(true, true, false)]();
d[new Cond(false, true, true)]();
d[new Cond(true, true, true)]();
}
public class Cond : Tuple<bool, bool, bool>
{
public Cond(bool waterproof, bool shockproof, bool freezeproof) : base(waterproof, shockproof, freezeproof)
{
}
}
выход:
Action1
Action2
Action3
подкласс Tuple<>
потому что:
это делает все гораздо более читаемым, а не иметь общий споры повсюду.
вы можете использовать именованные параметры, как я сделал, чтобы сделать логику карты очень ясной.
вы можете поменять его с помощью пользовательского класса, если вам нужна более сложная логика, например, различное количество условий в каждом состоянии.
вам, вероятно, придется переопределить Equals
и GetHashCode
в этом случае.
(Вам, очевидно, не нужно использовать анонимных делегатов; вы можете просто передать прямой ссылка на метод, который вы хотите использовать)
вы можете реализовать шаблон посетителя. Но это зависит от абстракции вашего уровня и ваших функций пример реализации шаблона посетителя
есть способ реализовать его без предложения if.
вы можете преобразовать это логическое значение в целое число, которое позволяет получить метод в словаре, например:
cond1 = false, cond2 = true, cond3 = false
вы можете преобразовать это в:
0 + 1 + 0
= 1
затем вы создадите ключ для каждого решения, которое у вас есть, следующим образом:
public Class Action1: IAction;
public Class Action2: IAction;
public Class Action3: IAction;
Dictionary<int, IAction> dict = new Dictionary<int, IAction>()
{
6, new Action1(); //110
3, new Action2(); //011
7, new Action3(); //111
};
интерфейс IAction:
public interface IAction
{
void execute();
}
и внутри каждого метода execute()
в конкретном классе, вы будете выполнять каждое действие.
тогда вы будете выполнять свое действие просто:
key = //transform binary in decimal
dict[key].execute();
надеюсь, что это поможет.
Это может быть метод расширения:
public static void WhenTrue(this bool condition, Action action)
{
if (action == null)
throw new ArgumentNullException("action");
if (condition)
action();
}
использование:
(condition1 && condition2).WhenTrue(() => DoSomeWork(param1));
но это имеет смысл только тогда, когда у вас есть много условий и много действий, чтобы сохранить код чище.