adamo94
adamo94

Reputation: 527

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 a class:

action1, action2, action3

action1 is called when conditions 1 and 2 are true.
action2 is called when conditions 2 and 3 are true.
action3 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.

Upvotes: 10

Views: 3244

Answers (6)

guijob
guijob

Reputation: 4488

There is a way you can implement it without any if clause.

You can transform this boolean in a integer which allows you get the method in a dictionary, for example:

cond1 = false, cond2 = true, cond3 = false

you can transform this in: 0 + 1 + 0 = 1

then, you will create a key for every solution you have, as follow:

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 interface:

public interface IAction
{
    void execute();
}

And inside each method execute() in concrete class, you will implement each action.

Then you will execute your action by simply:

key = //transform binary in decimal
dict[key].execute();

Hope this help.

Upvotes: 0

Whelkaholism
Whelkaholism

Reputation: 1495

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)

Upvotes: 1

MNF
MNF

Reputation: 717

You might implement visitor Pattern. But it depends on you level abstraction and your functionnalities Visitor Pattern Implementation example

Upvotes: 0

Sean
Sean

Reputation: 62542

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

Upvotes: 12

Peter B
Peter B

Reputation: 24280

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

Upvotes: 3

Dmitri Trofimov
Dmitri Trofimov

Reputation: 763

It could be an extension method:

public static void WhenTrue(this bool condition, Action action)
{
    if (action == null)
        throw new ArgumentNullException("action");

    if (condition)
        action();
}

usage:

(condition1 && condition2).WhenTrue(() => DoSomeWork(param1));

But this only makes sense when you have a lot of conditions and a lot of actions to keep the code cleaner.

Upvotes: -1

Related Questions