10

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.

RomCoo
  • 1,868
  • 2
  • 23
  • 36
adamo94
  • 507
  • 1
  • 6
  • 15
  • So you want a logical structure, that, when you run it, follow your explanations? – Mafii Apr 15 '16 at 08:51
  • Patterns? Design patterns are not conditional statements mate ;) – Tushar Gupta Apr 15 '16 at 08:51
  • 1
    make one static method that takes those bool vars and return correct Action... – Nino Apr 15 '16 at 08:52
  • @TusharGupta unless we are talking about the Interpreter patter. Or Command. Or Composite. In all of them one of the samples is composing a complex condition. But they aren't suitable here – Panagiotis Kanavos Apr 15 '16 at 08:52
  • @PanagiotisKanavos : Op mentioned and tagged design patterns and asking a replacement of if-else and Also he interpreter design pattern allow us to take the rules of a language and build them as classes. ;) – Tushar Gupta Apr 15 '16 at 08:54
  • In fact, there is such a thing, although it predates patterns. You are asking for a truth table whose first three columns containt the condition combinations and fourth column an *Action*. You could probably use a Dictionary> whose key would be the bitwise OR of the flags – Panagiotis Kanavos Apr 15 '16 at 08:55
  • just include your code.... how you see it, then SO can see how it can be changed... to look nicer. – Seabizkit Apr 15 '16 at 08:56
  • @TusharGupta exactly. And one of the examples in the *original pattern* was complex logical expressions. – Panagiotis Kanavos Apr 15 '16 at 08:56
  • @PanagiotisKanavos : So are ok with using something like DFS in place of if-else for such small cause ? – Tushar Gupta Apr 15 '16 at 08:59
  • 2
    "Action1 is called when conditions 1 and 2 are true". Does it also need condition3 to be false? – Hans Kesting Apr 15 '16 at 09:01
  • [Replace your conditions with polymorphism](https://sourcemaking.com/refactoring/replace-conditional-with-polymorphism). Show us your code if you want pointers on how to refactor. – Nathan Cooper Apr 15 '16 at 12:03
  • -1'd the answers that suggested how the OP would abstractly hammer conditions into their code, rather than say... designing things well. Read the link. – Nathan Cooper Apr 15 '16 at 12:10

6 Answers6

12

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);
  }
}
Sean
  • 60,939
  • 11
  • 97
  • 136
  • 1
    I'd love to know who downvoted a variation of Command? Do we *really* have to dig out the GOF copies and reference *pages* ? – Panagiotis Kanavos Apr 15 '16 at 08:58
  • @PanagiotisKanavos Is GOF really worth the read if you're a C# developer? I saw the examples are only C++ (sorry for going off-topic) – Alexander Derck Apr 15 '16 at 09:01
  • This can be made more generic. Eg, a single class could be used. The condition can become a `Func` field or property, the action an Action. In fact, that's how many workflow systems work (including WF). – Panagiotis Kanavos Apr 15 '16 at 09:01
  • 4
    @AlexanderDerck if you only show the examples, then you haven't read it. and only know a third-person summary of the patterns. Patterns are *not* the code, they are the problem, context, solution and the relations to other patterns. Several features of current languages like C# *are* implementations of those patterns. Knowing them will help you combine them in new ways – Panagiotis Kanavos Apr 15 '16 at 09:03
  • 1
    @PanagiotisKanavos So it's written in a form that's not too language-specific? That's what I needed to know because I see a lot of people recommending it :) – Alexander Derck Apr 15 '16 at 09:06
  • 3
    @AlexanderDerck some examples are in Smalltalk, some in C++. It just doesn't matter. It's the concepts that *really* matter, the description of the problems, solutions etc. You can easily find implementations of the various patterns in your favourite language once you understand what you are looking for – Panagiotis Kanavos Apr 15 '16 at 09:09
  • 1
    Hm, I think this is what I need. In this case, probably it's not possible to remove if statements. However it will help keep code clearer. – adamo94 Apr 15 '16 at 09:14
  • what if we have tens of conditions and more complex logic (a1 && (a2 || a3) && !(a4 && (a5 || a6)) ...)? – Ivan Yuriev Apr 15 '16 at 09:15
3

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

Community
  • 1
  • 1
Peter B
  • 22,460
  • 5
  • 32
  • 69
1

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)

Whelkaholism
  • 1,551
  • 3
  • 18
  • 28
0

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

MNF
  • 687
  • 9
  • 13
0

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.

guijob
  • 4,413
  • 3
  • 20
  • 39
-1

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.

Dmitri Trofimov
  • 753
  • 3
  • 14
  • 2
    How is that any better from saying `if(condition1 && condition2) DoSomeWork(param1);` ? – Sean Apr 15 '16 at 09:06
  • 2
    To be honest, it's not. However, if you change `void` to `bool` then it would be possible to chain... Although still pretty stupid :) Maybe the OP can think of something base on that =) – Dmitri Trofimov Apr 15 '16 at 09:14