longneck
longneck

Reputation: 12226

simplify huge if statements - design pattern?

i potentially have a set of if statements that look like this:

if (a and b and c and d) {
  // do stuff
} else (!a and b and c and d) {
  // do something else
} else (!a and !b and c and D) {
  // do yet something else
} ...

and so on for all possible permutations.

i thought of doing this:

switch ((a ? 'Y' : 'N') . (b ? 'Y' : 'N') . (c ? 'Y' : 'N') . (d ? 'Y' : 'N')) {

  case 'YNYN':
    // do stuff
    break;

  case 'NNNN':
    // etc.
    break;

}

is there a better way?

Upvotes: 6

Views: 2519

Answers (5)

TarkaDaal
TarkaDaal

Reputation: 19585

I would treat your four booleans as four bits, so as an integer between 0 and 15. I'd create an array with 16 elements, and store a function pointer in each element of the array. Every time you need to do this, I'd evaluate the booleans into a bit pattern, convert to int, and call the method stored in that index of the array.

I know you're asking about PHP, which I'm afraid I don't know. In C#, you could do something like this:

static class Multiplexer
{
    public static string Multiplex(bool a, bool b, bool c, bool d)
    {
        var i = 0;
        i |= (a ? 1 : 0) << 3;
        i |= (b ? 1 : 0) << 2;
        i |= (c ? 1 : 0) << 1;
        i |= (d ? 1 : 0);
        return _functions[i]();
    }

    private static Func<string>[] _functions = new Func<string>[] {
        () => { return "pie";},
        () => { return "index 1"; },
        () => { return DateTime.Now.ToString(); },
        () => { return "pie";},
        () => { return "index 1"; },
        () => { return DateTime.Now.ToString(); },
        () => { return Assembly.GetExecutingAssembly().FullName; },
        () => { return ""; },
        () => { return "pie";},
        () => { return "index 1"; },
        () => { return DateTime.Now.ToString(); },
        () => { return "pie";},
        () => { return "index 1"; },
        () => { return DateTime.Now.ToString(); },
        () => { return Assembly.GetExecutingAssembly().FullName; },
        () => { return ""; }};
}

Upvotes: 0

ircmaxell
ircmaxell

Reputation: 165261

What I would likely do (without knowing the specifics) is build a series of classes for each state. Then push the doStuff onto that class:

class DoStuff { //The Client
    protected $strategies = array();
    public function addStrategy(iDoStuffStrategy $strategy) {
        $this->strategies[] = $strategy;
    }
    public function doStuff ($a, $b, $c, $d) {
        foreach ($this->strategies as $strategy) {
            if ($strategy->test($a, $b, $c, $d)) {
                return $strategy->doStuff();
            }
        }
        throw new RuntimeException('Unhandleable Situation!');
    }
}

interface iDoStuffStrategy {
    // Return a bool if you can handle this situation
    public function test($a, $b, $c, $d);
    // Execute the implementation
    public function doStuff();
}

Then, each class would look like this:

public function StrategyFoo implements iDoStuffStrategy {
    public function test($a, $b, $c, $d) {
        return $a && $b && $c && $d;
    }
    public function doStuff() {
        //DoStuff!
    }
}
public function StrategyBar implements iDoStuffStrategy {
    public function test($a, $b, $c, $d) {
        return !$a && $b && $c && $d;
    }
    public function doStuff() {
        //DoStuff!
    }
}

It's basically an implementation of the Strategy Pattern. Doing it that way allows you to separate out the decision tree.

Upvotes: 9

Roger Halliburton
Roger Halliburton

Reputation: 2021

Yes, there is a better way.

Oh, did you want more detail than that? Well, you appear to have some sort of truth table with four variables. Are there 16 possible outcomes (2^4), or are you only interested in a subset? If there is one variable that has a roughly equal number of outcomes either way, perhaps use that as your topmost if statements, and use nested ifs.

if (b) {
  // cases where b is true
  if (...)
    ...
} else {
  // cases where b is false
  if (...)
    ...
}

You could also use a switch statement, but rather than a string made of Y and N use bitfields.

Upvotes: 0

dmcnelis
dmcnelis

Reputation: 2923

I took a similar approach to your case statement once when I was needing to aggregate data based on a set of conditions, of which there were five switches that could be on or off.

For dealing with aggregating information about the possible situations this worked fine, but outside of that use case, if there are truly n^2 different actions, then I'd stick with the multiple if statements. If there are not really that many permutations, I'd group the like results together to lessen the number of ifs.

Upvotes: 0

np-hard
np-hard

Reputation: 5815

I think you should considering solving this problem with decision trees, where different nodes are possible end states. then you can compose your problem in a tree, and get rid of all these ifs ....

Upvotes: 0

Related Questions