Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C#, patterns - many conditions

I am looking for a good pattern for my problem.

I have some bool variables:

condition1, condition2,condition3.

Also I have some actions, which are called in different places inside class:

action1,action2,action3

Action1 is called when conditions 1 and 2 are true. action2 is called when conditions 2 and 3 are true. Action 3 is called when all conditions are true.

Of course this is just a simplification of the problem. I don't want to use if else in every place. It is terribly unclear.

I have been thinking about state but I guess it's not best solution for this problem.

like image 595
adamo94 Avatar asked Apr 15 '16 08:04

adamo94


3 Answers

One option is to wrap the condition logic in a base class and then derive from it to execute the actual actions. The is a variation on the Command pattern and (I think) the Strategy pattern:

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);
  }
}
like image 130
Sean Avatar answered Oct 28 '22 12:10

Sean


You might be helped by an enum with the [Flags] attribute, instead of separate booleans. See this answer for a very good explanation + examples.

like image 32
Peter B Avatar answered Oct 28 '22 12:10

Peter B


Your conditions are not hugely well defined, but it sounds like a map of states to actions, where a state is defined by a number of simple conditions, and each state only has one action. So why not actually represent it like that?

Here's a simple LinqPad example:

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)
    {
    }
}

Output:

Action1
Action2
Action3

The subclass of Tuple<> is because:

  1. It makes everything much more readable rather than having the generic arguments everywhere.

  2. You can use named parameters as I have done to make the map logic very clear.

  3. You can swap it out with a custom class if you need more complex logic, like varying numbers of conditions in each state.

You will probably need to override Equals and GetHashCode in that case.

(You obviously don't need to use the anonymous delegates; you can just pass a direct reference to the method you want to use)

like image 33
Whelkaholism Avatar answered Oct 28 '22 12:10

Whelkaholism