В 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<> потому что:

  1. это делает все гораздо более читаемым, а не иметь общий споры повсюду.

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

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

вам, вероятно, придется переопределить 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));

но это имеет смысл только тогда, когда у вас есть много условий и много действий, чтобы сохранить код чище.