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.
brea开发者_如何学编程k;
}
is there a better way?
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.
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 ....
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.
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.
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 ""; }};
}
精彩评论